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