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::AnimationRangeValue;
1882
1883 if !start.0.name.is_none() {
1884 AnimationRangeEnd(AnimationRangeValue::timeline_range(
1887 start.0.name,
1888 LengthPercentage::hundred_percent(),
1889 ))
1890 } else {
1891 animation_range_end::single_value::get_initial_specified_value()
1893 }
1894 });
1895
1896 starts.push(start);
1897 ends.push(end);
1898 Ok(())
1899 })?;
1900
1901 Ok(expanded! {
1902 animation_range_start: animation_range_start::SpecifiedValue(starts.into()),
1903 animation_range_end: animation_range_end::SpecifiedValue(ends.into()),
1904 })
1905 }
1906
1907 impl<'a> ToCss for LonghandsToSerialize<'a> {
1908 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1909 where
1910 W: fmt::Write,
1911 {
1912 use crate::values::generics::Optional;
1913
1914 let starts = &self.animation_range_start.0;
1915 let ends = &self.animation_range_end.0;
1916
1917 if starts.len() != ends.len() {
1918 return Ok(());
1919 }
1920
1921 for (i, (start, end)) in std::iter::zip(starts.iter(), ends.iter()).enumerate() {
1922 if i != 0 {
1923 dest.write_str(", ")?;
1924 }
1925
1926 start.to_css(dest)?;
1927
1928 let can_omit = if start.0.name == end.0.name {
1929 match end.0.lp {
1930 Optional::Some(ref p) => p == &LengthPercentage::hundred_percent(),
1933 Optional::None => end.0.name.is_none(),
1935 }
1936 } else {
1937 false
1938 };
1939 if !can_omit {
1940 dest.write_char(' ')?;
1941 end.to_css(dest)?;
1942 }
1943 }
1944 Ok(())
1945 }
1946 }
1947}
1948
1949pub mod transition {
1950 pub use crate::properties::generated::shorthands::transition::*;
1951
1952 use super::*;
1953 use crate::properties::longhands::{
1954 transition_behavior, transition_delay, transition_duration, transition_property,
1955 transition_timing_function,
1956 };
1957 use crate::values::specified::TransitionProperty;
1958
1959 pub fn parse_value<'i, 't>(
1960 context: &ParserContext,
1961 input: &mut Parser<'i, 't>,
1962 ) -> Result<Longhands, ParseError<'i>> {
1963 struct SingleTransition {
1964 transition_property: transition_property::SingleSpecifiedValue,
1965 transition_duration: transition_duration::SingleSpecifiedValue,
1966 transition_timing_function: transition_timing_function::SingleSpecifiedValue,
1967 transition_delay: transition_delay::SingleSpecifiedValue,
1968 transition_behavior: transition_behavior::SingleSpecifiedValue,
1969 }
1970
1971 fn parse_one_transition<'i, 't>(
1972 context: &ParserContext,
1973 input: &mut Parser<'i, 't>,
1974 first: bool,
1975 ) -> Result<SingleTransition, ParseError<'i>> {
1976 let mut property = None;
1977 let mut duration = None;
1978 let mut timing_function = None;
1979 let mut delay = None;
1980 let mut behavior = None;
1981
1982 let mut parsed = 0;
1983 loop {
1984 parsed += 1;
1985
1986 try_parse_one!(
1987 context,
1988 input,
1989 duration,
1990 transition_duration::single_value::parse
1991 );
1992 try_parse_one!(
1993 context,
1994 input,
1995 timing_function,
1996 transition_timing_function::single_value::parse
1997 );
1998 try_parse_one!(context, input, delay, transition_delay::single_value::parse);
1999 try_parse_one!(
2000 context,
2001 input,
2002 behavior,
2003 transition_behavior::single_value::parse
2004 );
2005 if property.is_none() {
2006 if let Ok(value) = input.try_parse(|i| TransitionProperty::parse(context, i)) {
2007 property = Some(value);
2008 continue;
2009 }
2010
2011 if first && input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
2012 property = Some(TransitionProperty::none());
2013 continue;
2014 }
2015 }
2016
2017 parsed -= 1;
2018 break;
2019 }
2020
2021 if parsed != 0 {
2022 Ok(SingleTransition {
2023 transition_property: property.unwrap_or_else(
2024 transition_property::single_value::get_initial_specified_value,
2025 ),
2026 transition_duration: duration.unwrap_or_else(
2027 transition_duration::single_value::get_initial_specified_value,
2028 ),
2029 transition_timing_function: timing_function.unwrap_or_else(
2030 transition_timing_function::single_value::get_initial_specified_value,
2031 ),
2032 transition_delay: delay.unwrap_or_else(
2033 transition_delay::single_value::get_initial_specified_value,
2034 ),
2035 transition_behavior: behavior.unwrap_or_else(
2036 transition_behavior::single_value::get_initial_specified_value,
2037 ),
2038 })
2039 } else {
2040 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
2041 }
2042 }
2043
2044 let mut first = true;
2045 let mut has_transition_property_none = false;
2046 let results = input.parse_comma_separated(|i| {
2047 if has_transition_property_none {
2048 return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError));
2049 }
2050 let transition = parse_one_transition(context, i, first)?;
2051 first = false;
2052 has_transition_property_none = transition.transition_property.is_none();
2053 Ok(transition)
2054 })?;
2055
2056 let len = results.len();
2057 let mut property = Vec::with_capacity(len);
2058 let mut duration = Vec::with_capacity(len);
2059 let mut timing_function = Vec::with_capacity(len);
2060 let mut delay = Vec::with_capacity(len);
2061 let mut behavior = Vec::with_capacity(len);
2062 for result in results {
2063 property.push(result.transition_property);
2064 duration.push(result.transition_duration);
2065 timing_function.push(result.transition_timing_function);
2066 delay.push(result.transition_delay);
2067 behavior.push(result.transition_behavior);
2068 }
2069
2070 Ok(Longhands {
2071 transition_property: transition_property::SpecifiedValue(property.into()),
2072 transition_duration: transition_duration::SpecifiedValue(duration.into()),
2073 transition_timing_function: transition_timing_function::SpecifiedValue(
2074 timing_function.into(),
2075 ),
2076 transition_delay: transition_delay::SpecifiedValue(delay.into()),
2077 transition_behavior: transition_behavior::SpecifiedValue(behavior.into()),
2078 })
2079 }
2080
2081 impl<'a> ToCss for LonghandsToSerialize<'a> {
2082 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
2083 where
2084 W: fmt::Write,
2085 {
2086 use crate::Zero;
2087 use style_traits::values::SequenceWriter;
2088
2089 let len = self.transition_property.0.len();
2090 debug_assert_ne!(
2091 len, 0,
2092 "We should always have at least one transition-property, even if none"
2093 );
2094 if self.transition_duration.0.len() != len {
2095 return Ok(());
2096 }
2097 if self.transition_delay.0.len() != len {
2098 return Ok(());
2099 }
2100 if self.transition_timing_function.0.len() != len {
2101 return Ok(());
2102 }
2103 if self.transition_behavior.0.len() != len {
2104 return Ok(());
2105 }
2106 for i in 0..len {
2107 if i != 0 {
2108 dest.write_str(", ")?;
2109 }
2110
2111 let has_duration = !self.transition_duration.0[i].is_zero();
2112 let has_timing = !self.transition_timing_function.0[i].is_ease();
2113 let has_delay = !self.transition_delay.0[i].is_zero();
2114 let has_behavior = !self.transition_behavior.0[i].is_normal();
2115 let has_any = has_duration || has_timing || has_delay || has_behavior;
2116
2117 let mut writer = SequenceWriter::new(dest, " ");
2118 if !self.transition_property.0[i].is_all() || !has_any {
2119 writer.item(&self.transition_property.0[i])?;
2120 }
2121 if has_duration || has_delay {
2122 writer.item(&self.transition_duration.0[i])?;
2123 }
2124 if has_timing {
2125 writer.item(&self.transition_timing_function.0[i])?;
2126 }
2127 if has_delay {
2128 writer.item(&self.transition_delay.0[i])?;
2129 }
2130 if has_behavior {
2131 writer.item(&self.transition_behavior.0[i])?;
2132 }
2133 }
2134 Ok(())
2135 }
2136 }
2137}
2138
2139pub mod outline {
2140 pub use crate::properties::generated::shorthands::outline::*;
2141
2142 use super::*;
2143 use crate::properties::longhands::{outline_color, outline_style, outline_width};
2144
2145 pub fn parse_value<'i, 't>(
2146 context: &ParserContext,
2147 input: &mut Parser<'i, 't>,
2148 ) -> Result<Longhands, ParseError<'i>> {
2149 let _unused = context;
2150 let mut color = None;
2151 let mut style = None;
2152 let mut width = None;
2153 let mut parsed = 0;
2154 loop {
2155 parsed += 1;
2156 try_parse_one!(context, input, color, specified::Color::parse);
2157 try_parse_one!(context, input, style, outline_style::parse);
2158 try_parse_one!(context, input, width, outline_width::parse);
2159 parsed -= 1;
2160 break;
2161 }
2162 if parsed == 0 {
2163 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
2164 }
2165 Ok(expanded! {
2166 outline_color: unwrap_or_initial!(outline_color, color),
2167 outline_style: unwrap_or_initial!(outline_style, style),
2168 outline_width: unwrap_or_initial!(outline_width, width),
2169 })
2170 }
2171
2172 impl<'a> ToCss for LonghandsToSerialize<'a> {
2173 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
2174 where
2175 W: fmt::Write,
2176 {
2177 let mut writer = SequenceWriter::new(dest, " ");
2178 if *self.outline_color != outline_color::get_initial_specified_value() {
2179 writer.item(self.outline_color)?;
2180 }
2181 if *self.outline_style != outline_style::get_initial_specified_value() {
2182 writer.item(self.outline_style)?;
2183 }
2184 if *self.outline_width != outline_width::get_initial_specified_value() {
2185 writer.item(self.outline_width)?;
2186 }
2187 if !writer.has_written() {
2188 self.outline_style.to_css(dest)?;
2189 }
2190 Ok(())
2191 }
2192 }
2193}
2194
2195pub mod background_position {
2196 pub use crate::properties::generated::shorthands::background_position::*;
2197
2198 use super::*;
2199 use crate::properties::longhands::{background_position_x, background_position_y};
2200 use crate::values::specified::position::Position;
2201 use crate::values::specified::AllowQuirks;
2202
2203 pub fn parse_value<'i, 't>(
2204 context: &ParserContext,
2205 input: &mut Parser<'i, 't>,
2206 ) -> Result<Longhands, ParseError<'i>> {
2207 let mut position_x = Vec::with_capacity(1);
2208 let mut position_y = Vec::with_capacity(1);
2209 let mut any = false;
2210
2211 input.parse_comma_separated(|input| {
2212 let value = Position::parse_three_value_quirky(context, input, AllowQuirks::Yes)?;
2213 position_x.push(value.horizontal);
2214 position_y.push(value.vertical);
2215 any = true;
2216 Ok(())
2217 })?;
2218 if !any {
2219 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
2220 }
2221
2222 Ok(expanded! {
2223 background_position_x: background_position_x::SpecifiedValue(position_x.into()),
2224 background_position_y: background_position_y::SpecifiedValue(position_y.into()),
2225 })
2226 }
2227
2228 impl<'a> ToCss for LonghandsToSerialize<'a> {
2229 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
2230 where
2231 W: fmt::Write,
2232 {
2233 let len = self.background_position_x.0.len();
2234 if len == 0 || len != self.background_position_y.0.len() {
2235 return Ok(());
2236 }
2237 for i in 0..len {
2238 Position {
2239 horizontal: self.background_position_x.0[i].clone(),
2240 vertical: self.background_position_y.0[i].clone(),
2241 }
2242 .to_css(dest)?;
2243
2244 if i < len - 1 {
2245 dest.write_str(", ")?;
2246 }
2247 }
2248 Ok(())
2249 }
2250 }
2251}
2252
2253pub mod background {
2254 pub use crate::properties::generated::shorthands::background::*;
2255
2256 use super::*;
2257 use crate::properties::longhands::background_clip;
2258 use crate::properties::longhands::background_clip::single_value::computed_value::T as Clip;
2259 use crate::properties::longhands::background_origin::single_value::computed_value::T as Origin;
2260 use crate::properties::longhands::{
2261 background_attachment, background_color, background_image, background_origin,
2262 background_size,
2263 };
2264 use crate::properties::longhands::{
2265 background_position_x, background_position_y, background_repeat,
2266 };
2267 use crate::values::specified::{AllowQuirks, Color, Position, PositionComponent};
2268
2269 impl From<background_origin::single_value::SpecifiedValue>
2270 for background_clip::single_value::SpecifiedValue
2271 {
2272 fn from(
2273 origin: background_origin::single_value::SpecifiedValue,
2274 ) -> background_clip::single_value::SpecifiedValue {
2275 match origin {
2276 background_origin::single_value::SpecifiedValue::ContentBox => {
2277 background_clip::single_value::SpecifiedValue::ContentBox
2278 },
2279 background_origin::single_value::SpecifiedValue::PaddingBox => {
2280 background_clip::single_value::SpecifiedValue::PaddingBox
2281 },
2282 background_origin::single_value::SpecifiedValue::BorderBox => {
2283 background_clip::single_value::SpecifiedValue::BorderBox
2284 },
2285 }
2286 }
2287 }
2288
2289 pub fn parse_value<'i, 't>(
2290 context: &ParserContext,
2291 input: &mut Parser<'i, 't>,
2292 ) -> Result<Longhands, ParseError<'i>> {
2293 let mut background_color = None;
2294
2295 let mut background_image = Vec::with_capacity(1);
2296 let mut background_position_x = Vec::with_capacity(1);
2297 let mut background_position_y = Vec::with_capacity(1);
2298 let mut background_repeat = Vec::with_capacity(1);
2299 let mut background_size = Vec::with_capacity(1);
2300 let mut background_attachment = Vec::with_capacity(1);
2301 let mut background_origin = Vec::with_capacity(1);
2302 let mut background_clip = Vec::with_capacity(1);
2303 input.parse_comma_separated(|input| {
2304 if background_color.is_some() {
2305 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
2306 }
2307
2308 let mut image = None;
2309 let mut position = None;
2310 let mut repeat = None;
2311 let mut size = None;
2312 let mut attachment = None;
2313 let mut origin = None;
2314 let mut clip = None;
2315 let mut parsed = 0;
2316 loop {
2317 parsed += 1;
2318 try_parse_one!(context, input, background_color, Color::parse);
2319 if position.is_none() {
2320 if let Ok(value) = input.try_parse(|input| {
2321 Position::parse_three_value_quirky(context, input, AllowQuirks::No)
2322 }) {
2323 position = Some(value);
2324
2325 size = input
2326 .try_parse(|input| {
2327 input.expect_delim('/')?;
2328 background_size::single_value::parse(context, input)
2329 })
2330 .ok();
2331
2332 continue;
2333 }
2334 }
2335 try_parse_one!(context, input, image, background_image::single_value::parse);
2336 try_parse_one!(
2337 context,
2338 input,
2339 repeat,
2340 background_repeat::single_value::parse
2341 );
2342 try_parse_one!(
2343 context,
2344 input,
2345 attachment,
2346 background_attachment::single_value::parse
2347 );
2348 try_parse_one!(
2349 context,
2350 input,
2351 origin,
2352 background_origin::single_value::parse
2353 );
2354 try_parse_one!(context, input, clip, background_clip::single_value::parse);
2355 parsed -= 1;
2356 break;
2357 }
2358 if parsed == 0 {
2359 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
2360 }
2361 if clip.is_none() {
2362 if let Some(origin) = origin {
2363 clip = Some(background_clip::single_value::SpecifiedValue::from(origin));
2364 }
2365 }
2366 if let Some(position) = position {
2367 background_position_x.push(position.horizontal);
2368 background_position_y.push(position.vertical);
2369 } else {
2370 background_position_x.push(PositionComponent::zero());
2371 background_position_y.push(PositionComponent::zero());
2372 }
2373 if let Some(bg_image) = image {
2374 background_image.push(bg_image);
2375 } else {
2376 background_image
2377 .push(background_image::single_value::get_initial_specified_value());
2378 }
2379 if let Some(bg_repeat) = repeat {
2380 background_repeat.push(bg_repeat);
2381 } else {
2382 background_repeat
2383 .push(background_repeat::single_value::get_initial_specified_value());
2384 }
2385 if let Some(bg_size) = size {
2386 background_size.push(bg_size);
2387 } else {
2388 background_size.push(background_size::single_value::get_initial_specified_value());
2389 }
2390 if let Some(bg_attachment) = attachment {
2391 background_attachment.push(bg_attachment);
2392 } else {
2393 background_attachment
2394 .push(background_attachment::single_value::get_initial_specified_value());
2395 }
2396 if let Some(bg_origin) = origin {
2397 background_origin.push(bg_origin);
2398 } else {
2399 background_origin
2400 .push(background_origin::single_value::get_initial_specified_value());
2401 }
2402 if let Some(bg_clip) = clip {
2403 background_clip.push(bg_clip);
2404 } else {
2405 background_clip.push(background_clip::single_value::get_initial_specified_value());
2406 }
2407 Ok(())
2408 })?;
2409
2410 Ok(expanded! {
2411 background_color: background_color.unwrap_or(Color::transparent()),
2412 background_image: background_image::SpecifiedValue(background_image.into()),
2413 background_position_x: background_position_x::SpecifiedValue(background_position_x.into()),
2414 background_position_y: background_position_y::SpecifiedValue(background_position_y.into()),
2415 background_repeat: background_repeat::SpecifiedValue(background_repeat.into()),
2416 background_size: background_size::SpecifiedValue(background_size.into()),
2417 background_attachment: background_attachment::SpecifiedValue(background_attachment.into()),
2418 background_origin: background_origin::SpecifiedValue(background_origin.into()),
2419 background_clip: background_clip::SpecifiedValue(background_clip.into()),
2420 })
2421 }
2422
2423 impl<'a> ToCss for LonghandsToSerialize<'a> {
2424 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
2425 where
2426 W: fmt::Write,
2427 {
2428 let len = self.background_image.0.len();
2429 if len == 0 {
2430 return Ok(());
2431 }
2432 if len != self.background_image.0.len() {
2433 return Ok(());
2434 }
2435 if len != self.background_position_x.0.len() {
2436 return Ok(());
2437 }
2438 if len != self.background_position_y.0.len() {
2439 return Ok(());
2440 }
2441 if len != self.background_size.0.len() {
2442 return Ok(());
2443 }
2444 if len != self.background_repeat.0.len() {
2445 return Ok(());
2446 }
2447 if len != self.background_origin.0.len() {
2448 return Ok(());
2449 }
2450 if len != self.background_clip.0.len() {
2451 return Ok(());
2452 }
2453 if len != self.background_attachment.0.len() {
2454 return Ok(());
2455 }
2456
2457 for i in 0..len {
2458 let image = &self.background_image.0[i];
2459 let position_x = &self.background_position_x.0[i];
2460 let position_y = &self.background_position_y.0[i];
2461 let repeat = &self.background_repeat.0[i];
2462 let size = &self.background_size.0[i];
2463 let attachment = &self.background_attachment.0[i];
2464 let origin = &self.background_origin.0[i];
2465 let clip = &self.background_clip.0[i];
2466
2467 if i != 0 {
2468 dest.write_str(", ")?;
2469 }
2470
2471 let mut writer = SequenceWriter::new(dest, " ");
2472 if *image != background_image::single_value::get_initial_specified_value() {
2473 writer.item(image)?;
2474 }
2475
2476 if *position_x != PositionComponent::zero()
2477 || *position_y != PositionComponent::zero()
2478 || *size != background_size::single_value::get_initial_specified_value()
2479 {
2480 writer.write_item(|dest| {
2481 Position {
2482 horizontal: position_x.clone(),
2483 vertical: position_y.clone(),
2484 }
2485 .to_css(dest)?;
2486 if *size != background_size::single_value::get_initial_specified_value() {
2487 dest.write_str(" / ")?;
2488 size.to_css(dest)?;
2489 }
2490 Ok(())
2491 })?;
2492 }
2493 if *repeat != background_repeat::single_value::get_initial_specified_value() {
2494 writer.item(repeat)?;
2495 }
2496 if *attachment != background_attachment::single_value::get_initial_specified_value()
2497 {
2498 writer.item(attachment)?;
2499 }
2500
2501 if *origin != Origin::PaddingBox || *clip != Clip::BorderBox {
2502 writer.item(origin)?;
2503 if *clip != From::from(*origin) {
2504 writer.item(clip)?;
2505 }
2506 }
2507
2508 if i == len - 1 {
2509 if *self.background_color != background_color::get_initial_specified_value() {
2510 writer.item(self.background_color)?;
2511 }
2512 }
2513
2514 if !writer.has_written() {
2515 image.to_css(dest)?;
2516 }
2517 }
2518
2519 Ok(())
2520 }
2521 }
2522}
2523
2524pub mod font {
2525 pub use crate::properties::generated::shorthands::font::*;
2526
2527 use super::*;
2528 #[cfg(feature = "gecko")]
2529 use crate::properties::longhands::{
2530 font_family, font_feature_settings, font_kerning, font_language_override, font_size,
2531 font_size_adjust, font_variant_alternates, font_variant_east_asian, font_variant_emoji,
2532 font_variant_ligatures, font_variant_numeric, font_variant_position,
2533 };
2534 use crate::properties::longhands::{
2535 font_optical_sizing, font_stretch, font_style, font_variant_caps, font_variation_settings,
2536 font_weight,
2537 };
2538 #[cfg(feature = "gecko")]
2539 use crate::values::specified::font::SystemFont;
2540 use crate::values::specified::font::{
2541 FontFamily, FontSize, FontStretch, FontStretchKeyword, FontStyle, FontWeight, LineHeight,
2542 };
2543
2544 pub fn parse_value<'i, 't>(
2545 context: &ParserContext,
2546 input: &mut Parser<'i, 't>,
2547 ) -> Result<Longhands, ParseError<'i>> {
2548 let mut nb_normals = 0;
2549 let mut style = None;
2550 let mut variant_caps = None;
2551 let mut weight = None;
2552 let mut stretch = None;
2553 #[cfg(feature = "gecko")]
2554 if let Ok(sys) = input.try_parse(|i| SystemFont::parse(context, i)) {
2555 return Ok(Longhands {
2556 font_family: font_family::SpecifiedValue::system_font(sys),
2557 font_size: font_size::SpecifiedValue::system_font(sys),
2558 font_style: font_style::SpecifiedValue::system_font(sys),
2559 font_stretch: font_stretch::SpecifiedValue::system_font(sys),
2560 font_weight: font_weight::SpecifiedValue::system_font(sys),
2561 line_height: LineHeight::normal(),
2562 font_kerning: font_kerning::get_initial_specified_value(),
2563 font_language_override: font_language_override::get_initial_specified_value(),
2564 font_size_adjust: font_size_adjust::get_initial_specified_value(),
2565 font_variant_alternates: font_variant_alternates::get_initial_specified_value(),
2566 font_variant_east_asian: font_variant_east_asian::get_initial_specified_value(),
2567 font_variant_emoji: font_variant_emoji::get_initial_specified_value(),
2568 font_variant_ligatures: font_variant_ligatures::get_initial_specified_value(),
2569 font_variant_numeric: font_variant_numeric::get_initial_specified_value(),
2570 font_variant_position: font_variant_position::get_initial_specified_value(),
2571 font_feature_settings: font_feature_settings::get_initial_specified_value(),
2572 font_optical_sizing: font_optical_sizing::get_initial_specified_value(),
2573 font_variant_caps: font_variant_caps::get_initial_specified_value(),
2574 font_variation_settings: font_variation_settings::get_initial_specified_value(),
2575 });
2576 }
2577
2578 let size;
2579 loop {
2580 if input
2581 .try_parse(|input| input.expect_ident_matching("normal"))
2582 .is_ok()
2583 {
2584 nb_normals += 1;
2585 continue;
2586 }
2587 try_parse_one!(context, input, style, font_style::parse);
2588 try_parse_one!(context, input, weight, font_weight::parse);
2589 if variant_caps.is_none() {
2590 if input
2591 .try_parse(|input| input.expect_ident_matching("small-caps"))
2592 .is_ok()
2593 {
2594 variant_caps = Some(font_variant_caps::SpecifiedValue::SmallCaps);
2595 continue;
2596 }
2597 }
2598 try_parse_one!(input, stretch, FontStretchKeyword::parse);
2599 size = FontSize::parse(context, input)?;
2600 break;
2601 }
2602
2603 let line_height = if input.try_parse(|input| input.expect_delim('/')).is_ok() {
2604 Some(LineHeight::parse(context, input)?)
2605 } else {
2606 None
2607 };
2608
2609 #[inline]
2610 fn count<T>(opt: &Option<T>) -> u8 {
2611 if opt.is_some() {
2612 1
2613 } else {
2614 0
2615 }
2616 }
2617
2618 if (count(&style) + count(&weight) + count(&variant_caps) + count(&stretch) + nb_normals)
2619 > 4
2620 {
2621 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
2622 }
2623
2624 let family = FontFamily::parse(context, input)?;
2625 let stretch = stretch.map(FontStretch::Keyword);
2626 Ok(Longhands {
2627 font_style: unwrap_or_initial!(font_style, style),
2628 font_weight: unwrap_or_initial!(font_weight, weight),
2629 font_stretch: unwrap_or_initial!(font_stretch, stretch),
2630 font_variant_caps: unwrap_or_initial!(font_variant_caps, variant_caps),
2631 font_size: size,
2632 line_height: line_height.unwrap_or(LineHeight::normal()),
2633 font_family: family,
2634 font_optical_sizing: font_optical_sizing::get_initial_specified_value(),
2635 font_variation_settings: font_variation_settings::get_initial_specified_value(),
2636 #[cfg(feature = "gecko")]
2637 font_kerning: font_kerning::get_initial_specified_value(),
2638 #[cfg(feature = "gecko")]
2639 font_language_override: font_language_override::get_initial_specified_value(),
2640 #[cfg(feature = "gecko")]
2641 font_size_adjust: font_size_adjust::get_initial_specified_value(),
2642 #[cfg(feature = "gecko")]
2643 font_variant_alternates: font_variant_alternates::get_initial_specified_value(),
2644 #[cfg(feature = "gecko")]
2645 font_variant_east_asian: font_variant_east_asian::get_initial_specified_value(),
2646 #[cfg(feature = "gecko")]
2647 font_variant_emoji: font_variant_emoji::get_initial_specified_value(),
2648 #[cfg(feature = "gecko")]
2649 font_variant_ligatures: font_variant_ligatures::get_initial_specified_value(),
2650 #[cfg(feature = "gecko")]
2651 font_variant_numeric: font_variant_numeric::get_initial_specified_value(),
2652 #[cfg(feature = "gecko")]
2653 font_variant_position: font_variant_position::get_initial_specified_value(),
2654 #[cfg(feature = "gecko")]
2655 font_feature_settings: font_feature_settings::get_initial_specified_value(),
2656 })
2657 }
2658
2659 #[cfg(feature = "gecko")]
2660 enum CheckSystemResult {
2661 AllSystem(SystemFont),
2662 SomeSystem,
2663 None,
2664 }
2665
2666 impl<'a> ToCss for LonghandsToSerialize<'a> {
2667 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
2668 where
2669 W: fmt::Write,
2670 {
2671 #[cfg(feature = "gecko")]
2672 match self.check_system() {
2673 CheckSystemResult::AllSystem(sys) => return sys.to_css(dest),
2674 CheckSystemResult::SomeSystem => return Ok(()),
2675 CheckSystemResult::None => {},
2676 }
2677
2678 if let Some(v) = self.font_optical_sizing {
2679 if v != &font_optical_sizing::get_initial_specified_value() {
2680 return Ok(());
2681 }
2682 }
2683 if let Some(v) = self.font_variation_settings {
2684 if v != &font_variation_settings::get_initial_specified_value() {
2685 return Ok(());
2686 }
2687 }
2688 #[cfg(feature = "gecko")]
2689 if let Some(v) = self.font_variant_emoji {
2690 if v != &font_variant_emoji::get_initial_specified_value() {
2691 return Ok(());
2692 }
2693 }
2694
2695 #[cfg(feature = "gecko")]
2696 if self.font_kerning != &font_kerning::get_initial_specified_value() {
2697 return Ok(());
2698 }
2699 #[cfg(feature = "gecko")]
2700 if self.font_language_override != &font_language_override::get_initial_specified_value()
2701 {
2702 return Ok(());
2703 }
2704 #[cfg(feature = "gecko")]
2705 if self.font_size_adjust != &font_size_adjust::get_initial_specified_value() {
2706 return Ok(());
2707 }
2708 #[cfg(feature = "gecko")]
2709 if self.font_variant_alternates
2710 != &font_variant_alternates::get_initial_specified_value()
2711 {
2712 return Ok(());
2713 }
2714 #[cfg(feature = "gecko")]
2715 if self.font_variant_east_asian
2716 != &font_variant_east_asian::get_initial_specified_value()
2717 {
2718 return Ok(());
2719 }
2720 #[cfg(feature = "gecko")]
2721 if self.font_variant_ligatures != &font_variant_ligatures::get_initial_specified_value()
2722 {
2723 return Ok(());
2724 }
2725 #[cfg(feature = "gecko")]
2726 if self.font_variant_numeric != &font_variant_numeric::get_initial_specified_value() {
2727 return Ok(());
2728 }
2729 #[cfg(feature = "gecko")]
2730 if self.font_variant_position != &font_variant_position::get_initial_specified_value() {
2731 return Ok(());
2732 }
2733 #[cfg(feature = "gecko")]
2734 if self.font_feature_settings != &font_feature_settings::get_initial_specified_value() {
2735 return Ok(());
2736 }
2737
2738 let font_stretch = match *self.font_stretch {
2739 FontStretch::Keyword(kw) => kw,
2740 FontStretch::Stretch(percentage) => {
2741 match FontStretchKeyword::from_percentage(percentage.0.get()) {
2742 Some(kw) => kw,
2743 None => return Ok(()),
2744 }
2745 },
2746 FontStretch::System(..) => return Ok(()),
2747 };
2748
2749 if self.font_variant_caps != &font_variant_caps::get_initial_specified_value()
2750 && *self.font_variant_caps != font_variant_caps::SpecifiedValue::SmallCaps
2751 {
2752 return Ok(());
2753 }
2754
2755 if self.font_style != &font_style::get_initial_specified_value() {
2756 self.font_style.to_css(dest)?;
2757 dest.write_char(' ')?;
2758 }
2759 if self.font_variant_caps != &font_variant_caps::get_initial_specified_value() {
2760 self.font_variant_caps.to_css(dest)?;
2761 dest.write_char(' ')?;
2762 }
2763
2764 if self.font_weight != &FontWeight::normal()
2765 && self.font_weight != &FontWeight::from_gecko_keyword(400)
2766 {
2767 self.font_weight.to_css(dest)?;
2768 dest.write_char(' ')?;
2769 }
2770
2771 if font_stretch != FontStretchKeyword::Normal {
2772 font_stretch.to_css(dest)?;
2773 dest.write_char(' ')?;
2774 }
2775
2776 self.font_size.to_css(dest)?;
2777
2778 if *self.line_height != LineHeight::normal() {
2779 dest.write_str(" / ")?;
2780 self.line_height.to_css(dest)?;
2781 }
2782
2783 dest.write_char(' ')?;
2784 self.font_family.to_css(dest)?;
2785
2786 Ok(())
2787 }
2788 }
2789
2790 impl<'a> LonghandsToSerialize<'a> {
2791 #[cfg(feature = "gecko")]
2792 fn check_system(&self) -> CheckSystemResult {
2793 let mut sys = None;
2794 let mut all = true;
2795
2796 macro_rules! check {
2797 ($v:expr) => {
2798 match $v.get_system() {
2799 Some(s) => {
2800 debug_assert!(sys.is_none() || s == sys.unwrap());
2801 sys = Some(s);
2802 },
2803 None => {
2804 all = false;
2805 },
2806 }
2807 };
2808 ($e:expr, $($es:expr),+) => { check!($e); check!($($es),*); };
2809 }
2810
2811 check!(
2812 self.font_family,
2813 self.font_size,
2814 self.font_style,
2815 self.font_stretch,
2816 self.font_weight
2817 );
2818
2819 if self.line_height != &LineHeight::normal() {
2820 all = false
2821 }
2822 if all {
2823 CheckSystemResult::AllSystem(sys.unwrap())
2824 } else if sys.is_some() {
2825 CheckSystemResult::SomeSystem
2826 } else {
2827 CheckSystemResult::None
2828 }
2829 }
2830 }
2831
2832 impl SpecifiedValueInfo for Longhands {
2833 const SUPPORTED_TYPES: u8 = FontStyle::SUPPORTED_TYPES
2834 | FontWeight::SUPPORTED_TYPES
2835 | FontStretch::SUPPORTED_TYPES
2836 | font_variant_caps::SpecifiedValue::SUPPORTED_TYPES
2837 | FontSize::SUPPORTED_TYPES
2838 | FontFamily::SUPPORTED_TYPES;
2839
2840 fn collect_completion_keywords(f: KeywordsCollectFn) {
2841 FontStyle::collect_completion_keywords(f);
2842 FontWeight::collect_completion_keywords(f);
2843 FontStretch::collect_completion_keywords(f);
2844 font_variant_caps::SpecifiedValue::collect_completion_keywords(f);
2845 FontSize::collect_completion_keywords(f);
2846 FontFamily::collect_completion_keywords(f);
2847
2848 #[cfg(feature = "gecko")]
2849 SystemFont::collect_completion_keywords(f);
2850 }
2851 }
2852}
2853
2854pub mod font_variant {
2855 pub use crate::properties::generated::shorthands::font_variant::*;
2856
2857 use super::*;
2858 use crate::properties::longhands::font_variant_caps;
2859 #[cfg(feature = "gecko")]
2860 use crate::properties::longhands::{
2861 font_variant_alternates, font_variant_east_asian, font_variant_emoji,
2862 font_variant_ligatures, font_variant_numeric, font_variant_position,
2863 };
2864 #[allow(unused_imports)]
2865 use crate::values::specified::FontVariantLigatures;
2866
2867 pub fn parse_value<'i, 't>(
2868 context: &ParserContext,
2869 input: &mut Parser<'i, 't>,
2870 ) -> Result<Longhands, ParseError<'i>> {
2871 #[cfg(feature = "gecko")]
2872 let mut ligatures = None;
2873 let mut caps = None;
2874 #[cfg(feature = "gecko")]
2875 let mut alternates = None;
2876 #[cfg(feature = "gecko")]
2877 let mut numeric = None;
2878 #[cfg(feature = "gecko")]
2879 let mut east_asian = None;
2880 #[cfg(feature = "gecko")]
2881 let mut position = None;
2882 #[cfg(feature = "gecko")]
2883 let mut emoji = None;
2884
2885 if input
2886 .try_parse(|input| input.expect_ident_matching("normal"))
2887 .is_ok()
2888 {
2889 } else if input
2890 .try_parse(|input| input.expect_ident_matching("none"))
2891 .is_ok()
2892 {
2893 #[cfg(feature = "gecko")]
2894 {
2895 ligatures = Some(FontVariantLigatures::NONE);
2896 }
2897 } else {
2898 let mut parsed = 0;
2899 loop {
2900 parsed += 1;
2901 if input
2902 .try_parse(|input| input.expect_ident_matching("normal"))
2903 .is_ok()
2904 || input
2905 .try_parse(|input| input.expect_ident_matching("none"))
2906 .is_ok()
2907 {
2908 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
2909 }
2910 #[cfg(feature = "gecko")]
2911 try_parse_one!(context, input, ligatures, font_variant_ligatures::parse);
2912 try_parse_one!(context, input, caps, font_variant_caps::parse);
2913 #[cfg(feature = "gecko")]
2914 try_parse_one!(context, input, alternates, font_variant_alternates::parse);
2915 #[cfg(feature = "gecko")]
2916 try_parse_one!(context, input, numeric, font_variant_numeric::parse);
2917 #[cfg(feature = "gecko")]
2918 try_parse_one!(context, input, east_asian, font_variant_east_asian::parse);
2919 #[cfg(feature = "gecko")]
2920 try_parse_one!(context, input, position, font_variant_position::parse);
2921 #[cfg(feature = "gecko")]
2922 try_parse_one!(context, input, emoji, font_variant_emoji::parse);
2923 parsed -= 1;
2924 break;
2925 }
2926
2927 if parsed == 0 {
2928 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
2929 }
2930 }
2931
2932 #[cfg(feature = "gecko")]
2933 return Ok(expanded! {
2934 font_variant_ligatures: unwrap_or_initial!(font_variant_ligatures, ligatures),
2935 font_variant_caps: unwrap_or_initial!(font_variant_caps, caps),
2936 font_variant_alternates: unwrap_or_initial!(font_variant_alternates, alternates),
2937 font_variant_numeric: unwrap_or_initial!(font_variant_numeric, numeric),
2938 font_variant_east_asian: unwrap_or_initial!(font_variant_east_asian, east_asian),
2939 font_variant_position: unwrap_or_initial!(font_variant_position, position),
2940 font_variant_emoji: unwrap_or_initial!(font_variant_emoji, emoji),
2941 });
2942 #[cfg(feature = "servo")]
2943 return Ok(expanded! {
2944 font_variant_caps: unwrap_or_initial!(font_variant_caps, caps),
2945 });
2946 }
2947
2948 impl<'a> ToCss for LonghandsToSerialize<'a> {
2949 #[allow(unused_assignments)]
2950 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
2951 where
2952 W: fmt::Write,
2953 {
2954 #[cfg(feature = "gecko")]
2955 let has_none_ligatures = self.font_variant_ligatures == &FontVariantLigatures::NONE;
2956 #[cfg(feature = "servo")]
2957 let has_none_ligatures = false;
2958
2959 #[cfg(feature = "gecko")]
2960 const TOTAL_SUBPROPS: usize = 7;
2961 #[cfg(feature = "servo")]
2962 const TOTAL_SUBPROPS: usize = 1;
2963 let mut nb_normals = 0;
2964 macro_rules! count_normal {
2965 ($e: expr, $p: ident) => {
2966 if *$e == $p::get_initial_specified_value() {
2967 nb_normals += 1;
2968 }
2969 };
2970 ($v: ident) => {
2971 count_normal!(self.$v, $v);
2972 };
2973 }
2974 #[cfg(feature = "gecko")]
2975 count_normal!(font_variant_ligatures);
2976 count_normal!(font_variant_caps);
2977 #[cfg(feature = "gecko")]
2978 count_normal!(font_variant_alternates);
2979 #[cfg(feature = "gecko")]
2980 count_normal!(font_variant_numeric);
2981 #[cfg(feature = "gecko")]
2982 count_normal!(font_variant_east_asian);
2983 #[cfg(feature = "gecko")]
2984 count_normal!(font_variant_position);
2985 #[cfg(feature = "gecko")]
2986 if let Some(value) = self.font_variant_emoji {
2987 if value == &font_variant_emoji::get_initial_specified_value() {
2988 nb_normals += 1;
2989 }
2990 } else {
2991 nb_normals += 1;
2992 }
2993
2994 if nb_normals == TOTAL_SUBPROPS {
2995 return dest.write_str("normal");
2996 }
2997 if has_none_ligatures {
2998 if nb_normals == TOTAL_SUBPROPS - 1 {
2999 dest.write_str("none")?;
3000 }
3001 return Ok(());
3002 }
3003
3004 let mut writer = SequenceWriter::new(dest, " ");
3005 macro_rules! write {
3006 ($e: expr, $p: ident) => {
3007 if *$e != $p::get_initial_specified_value() {
3008 writer.item($e)?;
3009 }
3010 };
3011 ($v: ident) => {
3012 write!(self.$v, $v);
3013 };
3014 }
3015
3016 #[cfg(feature = "gecko")]
3017 write!(font_variant_ligatures);
3018 write!(font_variant_caps);
3019 #[cfg(feature = "gecko")]
3020 write!(font_variant_alternates);
3021 #[cfg(feature = "gecko")]
3022 write!(font_variant_numeric);
3023 #[cfg(feature = "gecko")]
3024 write!(font_variant_east_asian);
3025 #[cfg(feature = "gecko")]
3026 write!(font_variant_position);
3027 #[cfg(feature = "gecko")]
3028 if let Some(v) = self.font_variant_emoji {
3029 write!(v, font_variant_emoji);
3030 }
3031 Ok(())
3032 }
3033 }
3034}
3035
3036#[cfg(feature = "gecko")]
3037pub mod font_synthesis {
3038 pub use crate::properties::generated::shorthands::font_synthesis::*;
3039
3040 use super::*;
3041 use crate::values::specified::{FontSynthesis, FontSynthesisStyle};
3042
3043 pub fn parse_value<'i, 't>(
3044 _context: &ParserContext,
3045 input: &mut Parser<'i, 't>,
3046 ) -> Result<Longhands, ParseError<'i>> {
3047 let mut weight = FontSynthesis::None;
3048 let mut style = FontSynthesisStyle::None;
3049 let mut small_caps = FontSynthesis::None;
3050 let mut position = FontSynthesis::None;
3051
3052 if !input
3053 .try_parse(|input| input.expect_ident_matching("none"))
3054 .is_ok()
3055 {
3056 let mut has_custom_value = false;
3057 while !input.is_exhausted() {
3058 try_match_ident_ignore_ascii_case! { input,
3059 "weight" if weight == FontSynthesis::None => {
3060 has_custom_value = true;
3061 weight = FontSynthesis::Auto;
3062 continue;
3063 },
3064 "style" if style == FontSynthesisStyle::None => {
3065 has_custom_value = true;
3066 style = FontSynthesisStyle::Auto;
3067 continue;
3068 },
3069 "small-caps" if small_caps == FontSynthesis::None => {
3070 has_custom_value = true;
3071 small_caps = FontSynthesis::Auto;
3072 continue;
3073 },
3074 "position" if position == FontSynthesis::None => {
3075 has_custom_value = true;
3076 position = FontSynthesis::Auto;
3077 continue;
3078 },
3079 "oblique-only" if style == FontSynthesisStyle::None => {
3080 has_custom_value = true;
3081 style = FontSynthesisStyle::ObliqueOnly;
3082 continue;
3083 },
3084 }
3085 }
3086 if !has_custom_value {
3087 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
3088 }
3089 }
3090
3091 Ok(expanded! {
3092 font_synthesis_weight: weight,
3093 font_synthesis_style: style,
3094 font_synthesis_small_caps: small_caps,
3095 font_synthesis_position: position,
3096 })
3097 }
3098
3099 impl<'a> ToCss for LonghandsToSerialize<'a> {
3100 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
3101 where
3102 W: fmt::Write,
3103 {
3104 let mut writer = SequenceWriter::new(dest, " ");
3105 if self.font_synthesis_weight == &FontSynthesis::Auto {
3106 writer.raw_item("weight")?;
3107 }
3108 if self.font_synthesis_style != &FontSynthesisStyle::None {
3109 if self.font_synthesis_style == &FontSynthesisStyle::Auto {
3110 writer.raw_item("style")?;
3111 } else {
3112 writer.raw_item("oblique-only")?;
3113 }
3114 }
3115 if self.font_synthesis_small_caps == &FontSynthesis::Auto {
3116 writer.raw_item("small-caps")?;
3117 }
3118 if self.font_synthesis_position == &FontSynthesis::Auto {
3119 writer.raw_item("position")?;
3120 }
3121 if !writer.has_written() {
3122 writer.raw_item("none")?;
3123 }
3124 Ok(())
3125 }
3126 }
3127
3128 impl SpecifiedValueInfo for Longhands {
3131 fn collect_completion_keywords(f: KeywordsCollectFn) {
3132 f(&[
3133 "none",
3134 "oblique-only",
3135 "small-caps",
3136 "position",
3137 "style",
3138 "weight",
3139 ]);
3140 }
3141 }
3142}
3143
3144#[cfg(feature = "gecko")]
3145pub mod text_box {
3146 pub use crate::properties::generated::shorthands::text_box::*;
3147
3148 use super::*;
3149 use crate::values::specified::{TextBoxEdge, TextBoxTrim};
3150
3151 pub fn parse_value<'i>(
3152 context: &ParserContext,
3153 input: &mut Parser<'i, '_>,
3154 ) -> Result<Longhands, ParseError<'i>> {
3155 let mut trim = None;
3156 let mut edge = None;
3157
3158 if input
3159 .try_parse(|input| input.expect_ident_matching("normal"))
3160 .is_ok()
3161 {
3162 return Ok(Longhands {
3163 text_box_trim: TextBoxTrim::NONE,
3164 text_box_edge: TextBoxEdge::Auto,
3165 });
3166 }
3167
3168 loop {
3169 try_parse_one!(context, input, trim, TextBoxTrim::parse);
3170 try_parse_one!(context, input, edge, TextBoxEdge::parse);
3171 break;
3172 }
3173
3174 if trim.is_none() && edge.is_none() {
3175 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
3176 }
3177
3178 Ok(Longhands {
3183 text_box_trim: trim.unwrap_or(TextBoxTrim::TRIM_BOTH),
3184 text_box_edge: edge.unwrap_or(TextBoxEdge::Auto),
3185 })
3186 }
3187
3188 impl<'a> ToCss for LonghandsToSerialize<'a> {
3189 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
3190 where
3191 W: fmt::Write,
3192 {
3193 if *self.text_box_trim == TextBoxTrim::NONE && *self.text_box_edge == TextBoxEdge::Auto
3194 {
3195 return dest.write_str("normal");
3196 }
3197
3198 let mut writer = SequenceWriter::new(dest, " ");
3199 if *self.text_box_trim != specified::TextBoxTrim::TRIM_BOTH {
3200 writer.item(self.text_box_trim)?;
3201 }
3202 if *self.text_box_edge != specified::TextBoxEdge::Auto {
3203 writer.item(self.text_box_edge)?;
3204 }
3205 if !writer.has_written() {
3206 self.text_box_trim.to_css(dest)?;
3207 }
3208 Ok(())
3209 }
3210 }
3211}
3212
3213#[cfg(feature = "gecko")]
3214pub mod text_emphasis {
3215 pub use crate::properties::generated::shorthands::text_emphasis::*;
3216
3217 use super::*;
3218 use crate::properties::longhands::{text_emphasis_color, text_emphasis_style};
3219
3220 pub fn parse_value<'i, 't>(
3221 context: &ParserContext,
3222 input: &mut Parser<'i, 't>,
3223 ) -> Result<Longhands, ParseError<'i>> {
3224 let mut color = None;
3225 let mut style = None;
3226 let mut parsed = 0;
3227 loop {
3228 parsed += 1;
3229 try_parse_one!(context, input, color, text_emphasis_color::parse);
3230 try_parse_one!(context, input, style, text_emphasis_style::parse);
3231 parsed -= 1;
3232 break;
3233 }
3234 if parsed == 0 {
3235 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
3236 }
3237 Ok(expanded! {
3238 text_emphasis_color: unwrap_or_initial!(text_emphasis_color, color),
3239 text_emphasis_style: unwrap_or_initial!(text_emphasis_style, style),
3240 })
3241 }
3242}
3243
3244pub mod text_decoration {
3245 pub use crate::properties::generated::shorthands::text_decoration::*;
3246
3247 use super::*;
3248 #[cfg(feature = "gecko")]
3249 use crate::properties::longhands::text_decoration_thickness;
3250 use crate::properties::longhands::{
3251 text_decoration_color, text_decoration_line, text_decoration_style,
3252 };
3253
3254 pub fn parse_value<'i, 't>(
3255 context: &ParserContext,
3256 input: &mut Parser<'i, 't>,
3257 ) -> Result<Longhands, ParseError<'i>> {
3258 let mut line = None;
3259 let mut style = None;
3260 let mut color = None;
3261 #[cfg(feature = "gecko")]
3262 let mut thickness = None;
3263
3264 let mut parsed = 0;
3265 loop {
3266 parsed += 1;
3267 try_parse_one!(context, input, line, text_decoration_line::parse);
3268 try_parse_one!(context, input, style, text_decoration_style::parse);
3269 try_parse_one!(context, input, color, text_decoration_color::parse);
3270 #[cfg(feature = "gecko")]
3271 try_parse_one!(context, input, thickness, text_decoration_thickness::parse);
3272 parsed -= 1;
3273 break;
3274 }
3275
3276 if parsed == 0 {
3277 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
3278 }
3279
3280 #[cfg(feature = "gecko")]
3281 return Ok(expanded! {
3282 text_decoration_line: unwrap_or_initial!(text_decoration_line, line),
3283 text_decoration_style: unwrap_or_initial!(text_decoration_style, style),
3284 text_decoration_color: unwrap_or_initial!(text_decoration_color, color),
3285 text_decoration_thickness: unwrap_or_initial!(text_decoration_thickness, thickness),
3286 });
3287 #[cfg(feature = "servo")]
3288 return Ok(expanded! {
3289 text_decoration_line: unwrap_or_initial!(text_decoration_line, line),
3290 text_decoration_style: unwrap_or_initial!(text_decoration_style, style),
3291 text_decoration_color: unwrap_or_initial!(text_decoration_color, color),
3292 });
3293 }
3294
3295 impl<'a> ToCss for LonghandsToSerialize<'a> {
3296 #[allow(unused)]
3297 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
3298 where
3299 W: fmt::Write,
3300 {
3301 use crate::values::specified::Color;
3302 use crate::values::specified::TextDecorationLine;
3303
3304 let is_solid_style =
3305 *self.text_decoration_style == text_decoration_style::SpecifiedValue::Solid;
3306 let is_current_color = *self.text_decoration_color == Color::CurrentColor;
3307 #[cfg(feature = "gecko")]
3308 let is_auto_thickness = self.text_decoration_thickness.is_auto();
3309 #[cfg(feature = "servo")]
3310 let is_auto_thickness = true;
3311 let is_none = *self.text_decoration_line == TextDecorationLine::none();
3312
3313 let mut writer = SequenceWriter::new(dest, " ");
3314 if (is_solid_style && is_current_color && is_auto_thickness) || !is_none {
3315 writer.item(self.text_decoration_line)?;
3316 }
3317 #[cfg(feature = "gecko")]
3318 if !is_auto_thickness {
3319 writer.item(self.text_decoration_thickness)?;
3320 }
3321 if !is_solid_style {
3322 writer.item(self.text_decoration_style)?;
3323 }
3324 if !is_current_color {
3325 writer.item(self.text_decoration_color)?;
3326 }
3327 Ok(())
3328 }
3329 }
3330}
3331
3332pub mod animation {
3333 pub use crate::properties::generated::shorthands::animation::*;
3334
3335 use super::*;
3336 use crate::properties::longhands::{
3337 animation_delay, animation_direction, animation_duration, animation_fill_mode,
3338 animation_iteration_count, animation_name, animation_play_state, animation_timeline,
3339 animation_timing_function,
3340 };
3341
3342 pub fn parse_value<'i, 't>(
3343 context: &ParserContext,
3344 input: &mut Parser<'i, 't>,
3345 ) -> Result<Longhands, ParseError<'i>> {
3346 struct SingleAnimation {
3347 animation_name: animation_name::SingleSpecifiedValue,
3348 animation_duration: animation_duration::SingleSpecifiedValue,
3349 animation_timing_function: animation_timing_function::SingleSpecifiedValue,
3350 animation_delay: animation_delay::SingleSpecifiedValue,
3351 animation_iteration_count: animation_iteration_count::SingleSpecifiedValue,
3352 animation_direction: animation_direction::SingleSpecifiedValue,
3353 animation_fill_mode: animation_fill_mode::SingleSpecifiedValue,
3354 animation_play_state: animation_play_state::SingleSpecifiedValue,
3355 }
3356
3357 fn parse_one_animation<'i, 't>(
3358 context: &ParserContext,
3359 input: &mut Parser<'i, 't>,
3360 ) -> Result<SingleAnimation, ParseError<'i>> {
3361 let mut name = None;
3362 let mut duration = None;
3363 let mut timing_function = None;
3364 let mut delay = None;
3365 let mut iteration_count = None;
3366 let mut direction = None;
3367 let mut fill_mode = None;
3368 let mut play_state = None;
3369
3370 let mut parsed = 0;
3371 loop {
3372 parsed += 1;
3373 try_parse_one!(
3374 context,
3375 input,
3376 duration,
3377 animation_duration::single_value::parse
3378 );
3379 try_parse_one!(
3380 context,
3381 input,
3382 timing_function,
3383 animation_timing_function::single_value::parse
3384 );
3385 try_parse_one!(context, input, delay, animation_delay::single_value::parse);
3386 try_parse_one!(
3387 context,
3388 input,
3389 iteration_count,
3390 animation_iteration_count::single_value::parse
3391 );
3392 try_parse_one!(
3393 context,
3394 input,
3395 direction,
3396 animation_direction::single_value::parse
3397 );
3398 try_parse_one!(
3399 context,
3400 input,
3401 fill_mode,
3402 animation_fill_mode::single_value::parse
3403 );
3404 try_parse_one!(
3405 context,
3406 input,
3407 play_state,
3408 animation_play_state::single_value::parse
3409 );
3410 try_parse_one!(context, input, name, animation_name::single_value::parse);
3411 parsed -= 1;
3412 break;
3413 }
3414
3415 if parsed == 0 {
3416 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
3417 }
3418 Ok(SingleAnimation {
3419 animation_name: name
3420 .unwrap_or_else(animation_name::single_value::get_initial_specified_value),
3421 animation_duration: duration
3422 .unwrap_or_else(animation_duration::single_value::get_initial_specified_value),
3423 animation_timing_function: timing_function.unwrap_or_else(
3424 animation_timing_function::single_value::get_initial_specified_value,
3425 ),
3426 animation_delay: delay
3427 .unwrap_or_else(animation_delay::single_value::get_initial_specified_value),
3428 animation_iteration_count: iteration_count.unwrap_or_else(
3429 animation_iteration_count::single_value::get_initial_specified_value,
3430 ),
3431 animation_direction: direction
3432 .unwrap_or_else(animation_direction::single_value::get_initial_specified_value),
3433 animation_fill_mode: fill_mode
3434 .unwrap_or_else(animation_fill_mode::single_value::get_initial_specified_value),
3435 animation_play_state: play_state.unwrap_or_else(
3436 animation_play_state::single_value::get_initial_specified_value,
3437 ),
3438 })
3439 }
3440
3441 let mut names = vec![];
3442 let mut durations = vec![];
3443 let mut timing_functions = vec![];
3444 let mut delays = vec![];
3445 let mut iteration_counts = vec![];
3446 let mut directions = vec![];
3447 let mut fill_modes = vec![];
3448 let mut play_states = vec![];
3449
3450 let results = input.parse_comma_separated(|i| parse_one_animation(context, i))?;
3451 for result in results.into_iter() {
3452 names.push(result.animation_name);
3453 durations.push(result.animation_duration);
3454 timing_functions.push(result.animation_timing_function);
3455 delays.push(result.animation_delay);
3456 iteration_counts.push(result.animation_iteration_count);
3457 directions.push(result.animation_direction);
3458 fill_modes.push(result.animation_fill_mode);
3459 play_states.push(result.animation_play_state);
3460 }
3461
3462 Ok(expanded! {
3463 animation_name: animation_name::SpecifiedValue(names.into()),
3464 animation_duration: animation_duration::SpecifiedValue(durations.into()),
3465 animation_timing_function: animation_timing_function::SpecifiedValue(timing_functions.into()),
3466 animation_delay: animation_delay::SpecifiedValue(delays.into()),
3467 animation_iteration_count: animation_iteration_count::SpecifiedValue(iteration_counts.into()),
3468 animation_direction: animation_direction::SpecifiedValue(directions.into()),
3469 animation_fill_mode: animation_fill_mode::SpecifiedValue(fill_modes.into()),
3470 animation_play_state: animation_play_state::SpecifiedValue(play_states.into()),
3471 animation_timeline: animation_timeline::SpecifiedValue(
3472 vec![animation_timeline::single_value::get_initial_specified_value()].into()
3473 ),
3474 })
3475 }
3476
3477 impl<'a> ToCss for LonghandsToSerialize<'a> {
3478 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
3479 where
3480 W: fmt::Write,
3481 {
3482 use crate::values::specified::easing::TimingFunction;
3483 use crate::values::specified::{
3484 AnimationDirection, AnimationFillMode, AnimationPlayState,
3485 };
3486 use crate::Zero;
3487 use style_traits::values::SequenceWriter;
3488
3489 let len = self.animation_name.0.len();
3490 if len == 0 {
3491 return Ok(());
3492 }
3493
3494 if len != self.animation_duration.0.len() {
3495 return Ok(());
3496 }
3497 if len != self.animation_timing_function.0.len() {
3498 return Ok(());
3499 }
3500 if len != self.animation_delay.0.len() {
3501 return Ok(());
3502 }
3503 if len != self.animation_iteration_count.0.len() {
3504 return Ok(());
3505 }
3506 if len != self.animation_direction.0.len() {
3507 return Ok(());
3508 }
3509 if len != self.animation_fill_mode.0.len() {
3510 return Ok(());
3511 }
3512 if len != self.animation_play_state.0.len() {
3513 return Ok(());
3514 }
3515
3516 if self
3517 .animation_timeline
3518 .map_or(false, |v| v.0.len() != 1 || !v.0[0].is_auto())
3519 {
3520 return Ok(());
3521 }
3522
3523 for i in 0..len {
3524 if i != 0 {
3525 dest.write_str(", ")?;
3526 }
3527
3528 let has_duration = !self.animation_duration.0[i].is_auto()
3529 && !self.animation_duration.0[i].is_zero();
3530 let has_timing_function = !self.animation_timing_function.0[i].is_ease();
3531 let has_delay = !self.animation_delay.0[i].is_zero();
3532 let has_iteration_count = !self.animation_iteration_count.0[i].is_one();
3533 let has_direction =
3534 !matches!(self.animation_direction.0[i], AnimationDirection::Normal);
3535 let has_fill_mode =
3536 !matches!(self.animation_fill_mode.0[i], AnimationFillMode::None);
3537 let has_play_state =
3538 !matches!(self.animation_play_state.0[i], AnimationPlayState::Running);
3539 let animation_name = &self.animation_name.0[i];
3540 let has_name = !animation_name.is_none();
3541
3542 let mut writer = SequenceWriter::new(dest, " ");
3543
3544 if has_duration || has_delay {
3545 writer.item(&self.animation_duration.0[i])?;
3546 }
3547
3548 if has_timing_function || TimingFunction::match_keywords(animation_name) {
3549 writer.item(&self.animation_timing_function.0[i])?;
3550 }
3551
3552 if has_delay {
3553 writer.item(&self.animation_delay.0[i])?;
3554 }
3555 if has_iteration_count {
3556 writer.item(&self.animation_iteration_count.0[i])?;
3557 }
3558
3559 if has_direction || AnimationDirection::match_keywords(animation_name) {
3560 writer.item(&self.animation_direction.0[i])?;
3561 }
3562
3563 if has_fill_mode || AnimationFillMode::match_keywords(animation_name) {
3564 writer.item(&self.animation_fill_mode.0[i])?;
3565 }
3566
3567 if has_play_state || AnimationPlayState::match_keywords(animation_name) {
3568 writer.item(&self.animation_play_state.0[i])?;
3569 }
3570
3571 if has_name || !writer.has_written() {
3572 writer.item(animation_name)?;
3573 }
3574 }
3575 Ok(())
3576 }
3577 }
3578}
3579
3580#[cfg(feature = "gecko")]
3581pub mod mask {
3582 pub use crate::properties::generated::shorthands::mask::*;
3583
3584 use super::*;
3585 use crate::parser::Parse;
3586 use crate::properties::longhands::{
3587 mask_clip, mask_composite, mask_mode, mask_origin, mask_position_x, mask_position_y,
3588 mask_repeat,
3589 };
3590 use crate::properties::longhands::{mask_image, mask_size};
3591 use crate::values::specified::{Position, PositionComponent};
3592
3593 impl From<mask_origin::single_value::SpecifiedValue> for mask_clip::single_value::SpecifiedValue {
3594 fn from(
3595 origin: mask_origin::single_value::SpecifiedValue,
3596 ) -> mask_clip::single_value::SpecifiedValue {
3597 match origin {
3598 mask_origin::single_value::SpecifiedValue::ContentBox => {
3599 mask_clip::single_value::SpecifiedValue::ContentBox
3600 },
3601 mask_origin::single_value::SpecifiedValue::PaddingBox => {
3602 mask_clip::single_value::SpecifiedValue::PaddingBox
3603 },
3604 mask_origin::single_value::SpecifiedValue::BorderBox => {
3605 mask_clip::single_value::SpecifiedValue::BorderBox
3606 },
3607 mask_origin::single_value::SpecifiedValue::FillBox => {
3608 mask_clip::single_value::SpecifiedValue::FillBox
3609 },
3610 mask_origin::single_value::SpecifiedValue::StrokeBox => {
3611 mask_clip::single_value::SpecifiedValue::StrokeBox
3612 },
3613 mask_origin::single_value::SpecifiedValue::ViewBox => {
3614 mask_clip::single_value::SpecifiedValue::ViewBox
3615 },
3616 }
3617 }
3618 }
3619
3620 pub fn parse_value<'i, 't>(
3621 context: &ParserContext,
3622 input: &mut Parser<'i, 't>,
3623 ) -> Result<Longhands, ParseError<'i>> {
3624 let mut mask_image = Vec::with_capacity(1);
3625 let mut mask_mode = Vec::with_capacity(1);
3626 let mut mask_position_x = Vec::with_capacity(1);
3627 let mut mask_position_y = Vec::with_capacity(1);
3628 let mut mask_size = Vec::with_capacity(1);
3629 let mut mask_repeat = Vec::with_capacity(1);
3630 let mut mask_origin = Vec::with_capacity(1);
3631 let mut mask_clip = Vec::with_capacity(1);
3632 let mut mask_composite = Vec::with_capacity(1);
3633
3634 input.parse_comma_separated(|input| {
3635 let mut image = None;
3636 let mut mode = None;
3637 let mut position = None;
3638 let mut size = None;
3639 let mut repeat = None;
3640 let mut origin = None;
3641 let mut clip = None;
3642 let mut composite = None;
3643 let mut parsed = 0;
3644 loop {
3645 parsed += 1;
3646
3647 try_parse_one!(context, input, image, mask_image::single_value::parse);
3648 if position.is_none() {
3649 if let Ok(value) = input.try_parse(|input| Position::parse(context, input)) {
3650 position = Some(value);
3651 size = input
3652 .try_parse(|input| {
3653 input.expect_delim('/')?;
3654 mask_size::single_value::parse(context, input)
3655 })
3656 .ok();
3657
3658 continue;
3659 }
3660 }
3661 try_parse_one!(context, input, repeat, mask_repeat::single_value::parse);
3662 try_parse_one!(context, input, origin, mask_origin::single_value::parse);
3663 try_parse_one!(context, input, clip, mask_clip::single_value::parse);
3664 try_parse_one!(
3665 context,
3666 input,
3667 composite,
3668 mask_composite::single_value::parse
3669 );
3670 try_parse_one!(context, input, mode, mask_mode::single_value::parse);
3671
3672 parsed -= 1;
3673 break;
3674 }
3675 if clip.is_none() {
3676 if let Some(origin) = origin {
3677 clip = Some(mask_clip::single_value::SpecifiedValue::from(origin));
3678 }
3679 }
3680 if parsed == 0 {
3681 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
3682 }
3683 if let Some(position) = position {
3684 mask_position_x.push(position.horizontal);
3685 mask_position_y.push(position.vertical);
3686 } else {
3687 mask_position_x.push(PositionComponent::zero());
3688 mask_position_y.push(PositionComponent::zero());
3689 }
3690 if let Some(m_image) = image {
3691 mask_image.push(m_image);
3692 } else {
3693 mask_image.push(mask_image::single_value::get_initial_specified_value());
3694 }
3695 if let Some(m_mode) = mode {
3696 mask_mode.push(m_mode);
3697 } else {
3698 mask_mode.push(mask_mode::single_value::get_initial_specified_value());
3699 }
3700 if let Some(m_size) = size {
3701 mask_size.push(m_size);
3702 } else {
3703 mask_size.push(mask_size::single_value::get_initial_specified_value());
3704 }
3705 if let Some(m_repeat) = repeat {
3706 mask_repeat.push(m_repeat);
3707 } else {
3708 mask_repeat.push(mask_repeat::single_value::get_initial_specified_value());
3709 }
3710 if let Some(m_origin) = origin {
3711 mask_origin.push(m_origin);
3712 } else {
3713 mask_origin.push(mask_origin::single_value::get_initial_specified_value());
3714 }
3715 if let Some(m_clip) = clip {
3716 mask_clip.push(m_clip);
3717 } else {
3718 mask_clip.push(mask_clip::single_value::get_initial_specified_value());
3719 }
3720 if let Some(m_composite) = composite {
3721 mask_composite.push(m_composite);
3722 } else {
3723 mask_composite.push(mask_composite::single_value::get_initial_specified_value());
3724 }
3725 Ok(())
3726 })?;
3727
3728 Ok(expanded! {
3729 mask_image: mask_image::SpecifiedValue(mask_image.into()),
3730 mask_mode: mask_mode::SpecifiedValue(mask_mode.into()),
3731 mask_position_x: mask_position_x::SpecifiedValue(mask_position_x.into()),
3732 mask_position_y: mask_position_y::SpecifiedValue(mask_position_y.into()),
3733 mask_size: mask_size::SpecifiedValue(mask_size.into()),
3734 mask_repeat: mask_repeat::SpecifiedValue(mask_repeat.into()),
3735 mask_origin: mask_origin::SpecifiedValue(mask_origin.into()),
3736 mask_clip: mask_clip::SpecifiedValue(mask_clip.into()),
3737 mask_composite: mask_composite::SpecifiedValue(mask_composite.into()),
3738 })
3739 }
3740
3741 impl<'a> ToCss for LonghandsToSerialize<'a> {
3742 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
3743 where
3744 W: fmt::Write,
3745 {
3746 use crate::properties::longhands::mask_clip::single_value::computed_value::T as Clip;
3747 use crate::properties::longhands::mask_origin::single_value::computed_value::T as Origin;
3748 use style_traits::values::SequenceWriter;
3749
3750 let len = self.mask_image.0.len();
3751 if len == 0 {
3752 return Ok(());
3753 }
3754 if self.mask_mode.0.len() != len {
3755 return Ok(());
3756 }
3757 if self.mask_position_x.0.len() != len {
3758 return Ok(());
3759 }
3760 if self.mask_position_y.0.len() != len {
3761 return Ok(());
3762 }
3763 if self.mask_size.0.len() != len {
3764 return Ok(());
3765 }
3766 if self.mask_repeat.0.len() != len {
3767 return Ok(());
3768 }
3769 if self.mask_origin.0.len() != len {
3770 return Ok(());
3771 }
3772 if self.mask_clip.0.len() != len {
3773 return Ok(());
3774 }
3775 if self.mask_composite.0.len() != len {
3776 return Ok(());
3777 }
3778
3779 for i in 0..len {
3780 if i > 0 {
3781 dest.write_str(", ")?;
3782 }
3783
3784 let image = &self.mask_image.0[i];
3785 let mode = &self.mask_mode.0[i];
3786 let position_x = &self.mask_position_x.0[i];
3787 let position_y = &self.mask_position_y.0[i];
3788 let size = &self.mask_size.0[i];
3789 let repeat = &self.mask_repeat.0[i];
3790 let origin = &self.mask_origin.0[i];
3791 let clip = &self.mask_clip.0[i];
3792 let composite = &self.mask_composite.0[i];
3793
3794 let mut has_other = false;
3795 let has_image = *image != mask_image::single_value::get_initial_specified_value();
3796 has_other |= has_image;
3797 let has_mode = *mode != mask_mode::single_value::get_initial_specified_value();
3798 has_other |= has_mode;
3799 let has_size = *size != mask_size::single_value::get_initial_specified_value();
3800 has_other |= has_size;
3801 let has_repeat =
3802 *repeat != mask_repeat::single_value::get_initial_specified_value();
3803 has_other |= has_repeat;
3804 let has_composite =
3805 *composite != mask_composite::single_value::get_initial_specified_value();
3806 has_other |= has_composite;
3807 let has_position = *position_x != PositionComponent::zero()
3808 || *position_y != PositionComponent::zero();
3809 let has_origin = *origin != Origin::BorderBox;
3810 let has_clip = *clip != Clip::BorderBox;
3811
3812 if !has_other && !has_position && !has_origin && !has_clip {
3813 return image.to_css(dest);
3814 }
3815
3816 let mut writer = SequenceWriter::new(dest, " ");
3817 if has_image {
3818 writer.item(image)?;
3819 }
3820 if has_position || has_size {
3821 writer.write_item(|dest| {
3822 Position {
3823 horizontal: position_x.clone(),
3824 vertical: position_y.clone(),
3825 }
3826 .to_css(dest)?;
3827 if has_size {
3828 dest.write_str(" / ")?;
3829 size.to_css(dest)?;
3830 }
3831 Ok(())
3832 })?;
3833 }
3834
3835 if has_repeat {
3836 writer.item(repeat)?;
3837 }
3838
3839 if has_origin || (has_clip && *clip != Clip::NoClip) {
3840 writer.item(origin)?;
3841 }
3842
3843 if has_clip && *clip != From::from(*origin) {
3844 writer.item(clip)?;
3845 }
3846
3847 if has_composite {
3848 writer.item(composite)?;
3849 }
3850
3851 if has_mode {
3852 writer.item(mode)?;
3853 }
3854 }
3855
3856 Ok(())
3857 }
3858 }
3859}
3860
3861#[cfg(feature = "gecko")]
3862pub mod mask_position {
3863 pub use crate::properties::generated::shorthands::mask_position::*;
3864
3865 use super::*;
3866 use crate::properties::longhands::{mask_position_x, mask_position_y};
3867 use crate::values::specified::Position;
3868
3869 pub fn parse_value<'i, 't>(
3870 context: &ParserContext,
3871 input: &mut Parser<'i, 't>,
3872 ) -> Result<Longhands, ParseError<'i>> {
3873 let mut position_x = Vec::with_capacity(1);
3877 let mut position_y = Vec::with_capacity(1);
3878 input.parse_comma_separated(|input| {
3879 let value = Position::parse(context, input)?;
3880 position_x.push(value.horizontal);
3881 position_y.push(value.vertical);
3882 Ok(())
3883 })?;
3884
3885 if position_x.is_empty() {
3886 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
3887 }
3888
3889 Ok(expanded! {
3890 mask_position_x: mask_position_x::SpecifiedValue(position_x.into()),
3891 mask_position_y: mask_position_y::SpecifiedValue(position_y.into()),
3892 })
3893 }
3894
3895 impl<'a> ToCss for LonghandsToSerialize<'a> {
3896 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
3897 where
3898 W: fmt::Write,
3899 {
3900 let len = self.mask_position_x.0.len();
3901 if len == 0 || self.mask_position_y.0.len() != len {
3902 return Ok(());
3903 }
3904
3905 for i in 0..len {
3906 Position {
3907 horizontal: self.mask_position_x.0[i].clone(),
3908 vertical: self.mask_position_y.0[i].clone(),
3909 }
3910 .to_css(dest)?;
3911
3912 if i < len - 1 {
3913 dest.write_str(", ")?;
3914 }
3915 }
3916
3917 Ok(())
3918 }
3919 }
3920}
3921
3922pub mod grid_template {
3923 pub use crate::properties::generated::shorthands::grid_template::*;
3924
3925 use super::*;
3926 use crate::parser::Parse;
3927 use crate::values::generics::grid::{concat_serialize_idents, TrackListValue};
3928 use crate::values::generics::grid::{TrackList, TrackSize};
3929 use crate::values::specified::grid::parse_line_names;
3930 use crate::values::specified::position::{
3931 GridTemplateAreas, TemplateAreasArc, TemplateAreasParser,
3932 };
3933 use crate::values::specified::{GenericGridTemplateComponent, GridTemplateComponent};
3934 use servo_arc::Arc;
3935
3936 pub fn parse_grid_template<'i, 't>(
3937 context: &ParserContext,
3938 input: &mut Parser<'i, 't>,
3939 ) -> Result<
3940 (
3941 GridTemplateComponent,
3942 GridTemplateComponent,
3943 GridTemplateAreas,
3944 ),
3945 ParseError<'i>,
3946 > {
3947 if let Ok(x) = input.try_parse(|i| {
3948 if i.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
3949 if !i.is_exhausted() {
3950 return Err(());
3951 }
3952 return Ok((
3953 GenericGridTemplateComponent::None,
3954 GenericGridTemplateComponent::None,
3955 GridTemplateAreas::None,
3956 ));
3957 }
3958 Err(())
3959 }) {
3960 return Ok(x);
3961 }
3962
3963 let first_line_names = input.try_parse(parse_line_names).unwrap_or_default();
3964 let mut areas_parser = TemplateAreasParser::default();
3965 if areas_parser.try_parse_string(input).is_ok() {
3966 let mut values = vec![];
3967 let mut line_names = vec![];
3968 line_names.push(first_line_names);
3969 loop {
3970 let size = input
3971 .try_parse(|i| TrackSize::parse(context, i))
3972 .unwrap_or_default();
3973 values.push(TrackListValue::TrackSize(size));
3974 let mut names = input.try_parse(parse_line_names).unwrap_or_default();
3975 let more_names = input.try_parse(parse_line_names);
3976
3977 match areas_parser.try_parse_string(input) {
3978 Ok(()) => {
3979 if let Ok(v) = more_names {
3980 let mut names_vec = names.into_vec();
3981 names_vec.extend(v.into_iter());
3982 names = names_vec.into();
3983 }
3984 line_names.push(names);
3985 },
3986 Err(e) => {
3987 if more_names.is_ok() {
3988 return Err(e);
3989 }
3990 line_names.push(names);
3991 break;
3992 },
3993 };
3994 }
3995
3996 if line_names.len() == values.len() {
3997 line_names.push(Default::default());
3998 }
3999
4000 let template_areas = areas_parser
4001 .finish()
4002 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))?;
4003 let template_rows = TrackList {
4004 values: values.into(),
4005 line_names: line_names.into(),
4006 auto_repeat_index: std::usize::MAX,
4007 };
4008
4009 let template_cols = if input.try_parse(|i| i.expect_delim('/')).is_ok() {
4010 let value = GridTemplateComponent::parse_without_none(context, input)?;
4011 if let GenericGridTemplateComponent::TrackList(ref list) = value {
4012 if !list.is_explicit() {
4013 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
4014 }
4015 }
4016
4017 value
4018 } else {
4019 GridTemplateComponent::default()
4020 };
4021
4022 Ok((
4023 GenericGridTemplateComponent::TrackList(Box::new(template_rows)),
4024 template_cols,
4025 GridTemplateAreas::Areas(TemplateAreasArc(Arc::new(template_areas))),
4026 ))
4027 } else {
4028 let mut template_rows = GridTemplateComponent::parse(context, input)?;
4029 if let GenericGridTemplateComponent::TrackList(ref mut list) = template_rows {
4030 if list.line_names[0].is_empty() {
4031 list.line_names[0] = first_line_names;
4032 } else {
4033 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
4034 }
4035 }
4036
4037 input.expect_delim('/')?;
4038 Ok((
4039 template_rows,
4040 GridTemplateComponent::parse(context, input)?,
4041 GridTemplateAreas::None,
4042 ))
4043 }
4044 }
4045
4046 #[inline]
4047 pub fn parse_value<'i, 't>(
4048 context: &ParserContext,
4049 input: &mut Parser<'i, 't>,
4050 ) -> Result<Longhands, ParseError<'i>> {
4051 let (rows, columns, areas) = parse_grid_template(context, input)?;
4052 Ok(expanded! {
4053 grid_template_rows: rows,
4054 grid_template_columns: columns,
4055 grid_template_areas: areas,
4056 })
4057 }
4058
4059 pub fn serialize_grid_template<W>(
4060 template_rows: &GridTemplateComponent,
4061 template_columns: &GridTemplateComponent,
4062 template_areas: &GridTemplateAreas,
4063 dest: &mut CssWriter<W>,
4064 ) -> fmt::Result
4065 where
4066 W: fmt::Write,
4067 {
4068 match *template_areas {
4069 GridTemplateAreas::None => {
4070 if template_rows.is_initial() && template_columns.is_initial() {
4071 return GridTemplateComponent::default().to_css(dest);
4072 }
4073 template_rows.to_css(dest)?;
4074 dest.write_str(" / ")?;
4075 template_columns.to_css(dest)
4076 },
4077 GridTemplateAreas::Areas(ref areas) => {
4078 if areas.0.strings.len() != template_rows.track_list_len() {
4079 return Ok(());
4080 }
4081
4082 let track_list = match *template_rows {
4083 GenericGridTemplateComponent::TrackList(ref list) => {
4084 if !list.is_explicit() {
4085 return Ok(());
4086 }
4087 list
4088 },
4089 _ => return Ok(()),
4090 };
4091
4092 match *template_columns {
4093 GenericGridTemplateComponent::TrackList(ref list) => {
4094 if !list.is_explicit() {
4095 return Ok(());
4096 }
4097 },
4098 GenericGridTemplateComponent::Subgrid(_) => {
4099 return Ok(());
4100 },
4101 _ => {},
4102 }
4103
4104 let mut names_iter = track_list.line_names.iter();
4105 for (((i, string), names), value) in areas
4106 .0
4107 .strings
4108 .iter()
4109 .enumerate()
4110 .zip(&mut names_iter)
4111 .zip(track_list.values.iter())
4112 {
4113 if i > 0 {
4114 dest.write_char(' ')?;
4115 }
4116
4117 if !names.is_empty() {
4118 concat_serialize_idents("[", "] ", names, " ", dest)?;
4119 }
4120
4121 string.to_css(dest)?;
4122
4123 if !value.is_initial() {
4124 dest.write_char(' ')?;
4125 value.to_css(dest)?;
4126 }
4127 }
4128
4129 if let Some(names) = names_iter.next() {
4130 concat_serialize_idents(" [", "]", names, " ", dest)?;
4131 }
4132
4133 if let GenericGridTemplateComponent::TrackList(ref list) = *template_columns {
4134 dest.write_str(" / ")?;
4135 list.to_css(dest)?;
4136 }
4137
4138 Ok(())
4139 },
4140 }
4141 }
4142
4143 impl<'a> ToCss for LonghandsToSerialize<'a> {
4144 #[inline]
4145 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
4146 where
4147 W: fmt::Write,
4148 {
4149 serialize_grid_template(
4150 self.grid_template_rows,
4151 self.grid_template_columns,
4152 self.grid_template_areas,
4153 dest,
4154 )
4155 }
4156 }
4157}
4158
4159pub mod grid {
4160 pub use crate::properties::generated::shorthands::grid::*;
4161
4162 use super::*;
4163 use crate::parser::Parse;
4164 use crate::properties::longhands::{grid_auto_columns, grid_auto_flow, grid_auto_rows};
4165 use crate::values::generics::grid::GridTemplateComponent;
4166 use crate::values::specified::position::{GridAutoFlow, GridTemplateAreas};
4167 use crate::values::specified::{GenericGridTemplateComponent, ImplicitGridTracks};
4168
4169 pub fn parse_value<'i, 't>(
4170 context: &ParserContext,
4171 input: &mut Parser<'i, 't>,
4172 ) -> Result<Longhands, ParseError<'i>> {
4173 let mut temp_rows = GridTemplateComponent::default();
4174 let mut temp_cols = GridTemplateComponent::default();
4175 let mut temp_areas = GridTemplateAreas::None;
4176 let mut auto_rows = ImplicitGridTracks::default();
4177 let mut auto_cols = ImplicitGridTracks::default();
4178 let mut flow = grid_auto_flow::get_initial_value();
4179
4180 fn parse_auto_flow<'i, 't>(
4181 input: &mut Parser<'i, 't>,
4182 is_row: bool,
4183 ) -> Result<GridAutoFlow, ParseError<'i>> {
4184 let mut track = None;
4185 let mut dense = GridAutoFlow::empty();
4186
4187 for _ in 0..2 {
4188 if input
4189 .try_parse(|i| i.expect_ident_matching("auto-flow"))
4190 .is_ok()
4191 {
4192 track = if is_row {
4193 Some(GridAutoFlow::ROW)
4194 } else {
4195 Some(GridAutoFlow::COLUMN)
4196 };
4197 } else if input
4198 .try_parse(|i| i.expect_ident_matching("dense"))
4199 .is_ok()
4200 {
4201 dense = GridAutoFlow::DENSE
4202 } else {
4203 break;
4204 }
4205 }
4206
4207 if track.is_some() {
4208 Ok(track.unwrap() | dense)
4209 } else {
4210 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
4211 }
4212 }
4213
4214 if let Ok((rows, cols, areas)) =
4215 input.try_parse(|i| super::grid_template::parse_grid_template(context, i))
4216 {
4217 temp_rows = rows;
4218 temp_cols = cols;
4219 temp_areas = areas;
4220 } else if let Ok(rows) = input.try_parse(|i| GridTemplateComponent::parse(context, i)) {
4221 temp_rows = rows;
4222 input.expect_delim('/')?;
4223 flow = parse_auto_flow(input, false)?;
4224 auto_cols = input
4225 .try_parse(|i| grid_auto_columns::parse(context, i))
4226 .unwrap_or_default();
4227 } else {
4228 flow = parse_auto_flow(input, true)?;
4229 auto_rows = input
4230 .try_parse(|i| grid_auto_rows::parse(context, i))
4231 .unwrap_or_default();
4232 input.expect_delim('/')?;
4233 temp_cols = GridTemplateComponent::parse(context, input)?;
4234 }
4235
4236 Ok(expanded! {
4237 grid_template_rows: temp_rows,
4238 grid_template_columns: temp_cols,
4239 grid_template_areas: temp_areas,
4240 grid_auto_rows: auto_rows,
4241 grid_auto_columns: auto_cols,
4242 grid_auto_flow: flow,
4243 })
4244 }
4245
4246 impl<'a> LonghandsToSerialize<'a> {
4247 fn is_grid_template(&self) -> bool {
4248 self.grid_auto_rows.is_initial()
4249 && self.grid_auto_columns.is_initial()
4250 && *self.grid_auto_flow == grid_auto_flow::get_initial_value()
4251 }
4252 }
4253
4254 impl<'a> ToCss for LonghandsToSerialize<'a> {
4255 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
4256 where
4257 W: fmt::Write,
4258 {
4259 if self.is_grid_template() {
4260 return super::grid_template::serialize_grid_template(
4261 self.grid_template_rows,
4262 self.grid_template_columns,
4263 self.grid_template_areas,
4264 dest,
4265 );
4266 }
4267
4268 if *self.grid_template_areas != GridTemplateAreas::None {
4269 return Ok(());
4270 }
4271
4272 if self.grid_auto_flow.contains(GridAutoFlow::COLUMN) {
4273 if !self.grid_auto_rows.is_initial() || !self.grid_template_columns.is_initial() {
4274 return Ok(());
4275 }
4276
4277 if let GenericGridTemplateComponent::TrackList(ref list) = *self.grid_template_rows
4278 {
4279 if !list.is_explicit() {
4280 return Ok(());
4281 }
4282 }
4283
4284 self.grid_template_rows.to_css(dest)?;
4285 dest.write_str(" / auto-flow")?;
4286 if self.grid_auto_flow.contains(GridAutoFlow::DENSE) {
4287 dest.write_str(" dense")?;
4288 }
4289
4290 if !self.grid_auto_columns.is_initial() {
4291 dest.write_char(' ')?;
4292 self.grid_auto_columns.to_css(dest)?;
4293 }
4294
4295 return Ok(());
4296 }
4297
4298 if !self.grid_auto_columns.is_initial() || !self.grid_template_rows.is_initial() {
4299 return Ok(());
4300 }
4301
4302 if let GenericGridTemplateComponent::TrackList(ref list) = *self.grid_template_columns {
4303 if !list.is_explicit() {
4304 return Ok(());
4305 }
4306 }
4307
4308 dest.write_str("auto-flow")?;
4309 if self.grid_auto_flow.contains(GridAutoFlow::DENSE) {
4310 dest.write_str(" dense")?;
4311 }
4312
4313 if !self.grid_auto_rows.is_initial() {
4314 dest.write_char(' ')?;
4315 self.grid_auto_rows.to_css(dest)?;
4316 }
4317
4318 dest.write_str(" / ")?;
4319 self.grid_template_columns.to_css(dest)?;
4320 Ok(())
4321 }
4322 }
4323}