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