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