1use super::computed::transform::DirectionVector;
10use super::computed::{Context, ToComputedValue};
11use super::generics::grid::ImplicitGridTracks as GenericImplicitGridTracks;
12use super::generics::grid::{GridLine as GenericGridLine, TrackBreadth as GenericTrackBreadth};
13use super::generics::grid::{TrackList as GenericTrackList, TrackSize as GenericTrackSize};
14use super::generics::transform::IsParallelTo;
15use super::generics::{self, GreaterThanOrEqualToOne, NonNegative};
16use super::{CSSFloat, CSSInteger};
17use crate::context::QuirksMode;
18use crate::derives::*;
19use crate::parser::{Parse, ParserContext};
20use crate::values::specified::calc::CalcNode;
21use crate::values::{serialize_atom_identifier, serialize_number, AtomString};
22use crate::{Atom, Namespace, One, Prefix, Zero};
23use cssparser::{Parser, Token};
24use std::fmt::{self, Write};
25use std::ops::Add;
26use style_traits::values::specified::AllowedNumericType;
27use style_traits::{
28 CssString, CssWriter, NumericValue, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss,
29 ToTyped, TypedValue,
30};
31
32pub use self::align::{ContentDistribution, ItemPlacement, JustifyItems, SelfAlignment};
33pub use self::angle::{AllowUnitlessZeroAngle, Angle};
34pub use self::animation::{
35 AnimationComposition, AnimationDirection, AnimationDuration, AnimationFillMode,
36 AnimationIterationCount, AnimationName, AnimationPlayState, AnimationTimeline, ScrollAxis,
37 TimelineName, TransitionBehavior, TransitionProperty, ViewTimelineInset, ViewTransitionClass,
38 ViewTransitionName,
39};
40pub use self::background::{BackgroundRepeat, BackgroundSize};
41pub use self::basic_shape::FillRule;
42pub use self::border::{
43 BorderCornerRadius, BorderImageRepeat, BorderImageSideWidth, BorderImageSlice,
44 BorderImageWidth, BorderRadius, BorderSideOffset, BorderSideWidth, BorderSpacing, BorderStyle,
45 LineWidth,
46};
47pub use self::box_::{
48 Appearance, BaselineSource, BreakBetween, BreakWithin, Clear, Contain, ContainIntrinsicSize,
49 ContainerName, ContainerType, ContentVisibility, Display, Float, LineClamp, Overflow,
50 OverflowAnchor, OverflowClipMargin, OverscrollBehavior, Perspective, PositionProperty, Resize,
51 ScrollSnapAlign, ScrollSnapAxis, ScrollSnapStop, ScrollSnapStrictness, ScrollSnapType,
52 ScrollbarGutter, TouchAction, VerticalAlign, WillChange, WillChangeBits, WritingModeProperty,
53 Zoom,
54};
55pub use self::color::{
56 Color, ColorOrAuto, ColorPropertyValue, ColorScheme, ForcedColorAdjust, PrintColorAdjust,
57};
58pub use self::column::ColumnCount;
59pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset, CounterSet};
60pub use self::easing::TimingFunction;
61pub use self::effects::{BoxShadow, Filter, SimpleShadow};
62pub use self::flex::FlexBasis;
63pub use self::font::{FontFamily, FontLanguageOverride, FontPalette, FontStyle};
64pub use self::font::{FontFeatureSettings, FontVariantLigatures, FontVariantNumeric};
65pub use self::font::{
66 FontSize, FontSizeAdjust, FontSizeAdjustFactor, FontSizeKeyword, FontStretch, FontSynthesis,
67 FontSynthesisStyle,
68};
69pub use self::font::{FontVariantAlternates, FontWeight};
70pub use self::font::{FontVariantEastAsian, FontVariationSettings, LineHeight};
71pub use self::font::{MathDepth, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextScale};
72pub use self::image::{EndingShape as GradientEndingShape, Gradient, Image, ImageRendering};
73pub use self::length::{AbsoluteLength, CalcLengthPercentage, CharacterWidth};
74pub use self::length::{FontRelativeLength, Length, LengthOrNumber, NonNegativeLengthOrNumber};
75pub use self::length::{LengthOrAuto, LengthPercentage, LengthPercentageOrAuto};
76pub use self::length::{Margin, MaxSize, Size};
77pub use self::length::{NoCalcLength, ViewportPercentageLength, ViewportVariant};
78pub use self::length::{
79 NonNegativeLength, NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto,
80};
81#[cfg(feature = "gecko")]
82pub use self::list::ListStyleType;
83pub use self::list::Quotes;
84pub use self::motion::{OffsetPath, OffsetPosition, OffsetRotate};
85pub use self::outline::OutlineStyle;
86pub use self::page::{PageName, PageOrientation, PageSize, PageSizeOrientation, PaperSize};
87pub use self::percentage::{NonNegativePercentage, Percentage};
88pub use self::position::AnchorFunction;
89pub use self::position::AnchorName;
90pub use self::position::AnchorScope;
91pub use self::position::AspectRatio;
92pub use self::position::Inset;
93pub use self::position::PositionAnchor;
94pub use self::position::PositionTryFallbacks;
95pub use self::position::PositionTryOrder;
96pub use self::position::PositionVisibility;
97pub use self::position::{GridAutoFlow, GridTemplateAreas, Position, PositionOrAuto};
98pub use self::position::{MasonryAutoFlow, MasonryItemOrder, MasonryPlacement};
99pub use self::position::{PositionArea, PositionAreaKeyword};
100pub use self::position::{PositionComponent, ZIndex};
101pub use self::ratio::Ratio;
102pub use self::rect::NonNegativeLengthOrNumberRect;
103pub use self::resolution::Resolution;
104pub use self::svg::{DProperty, MozContextProperties};
105pub use self::svg::{SVGLength, SVGOpacity, SVGPaint};
106pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth, VectorEffect};
107pub use self::svg_path::SVGPathData;
108pub use self::text::RubyPosition;
109pub use self::text::{HyphenateCharacter, HyphenateLimitChars};
110pub use self::text::{InitialLetter, LetterSpacing, LineBreak, TextAlign, TextIndent};
111pub use self::text::{OverflowWrap, TextEmphasisPosition, TextEmphasisStyle, WordBreak};
112pub use self::text::{TextAlignKeyword, TextDecorationLine, TextOverflow, WordSpacing};
113pub use self::text::{TextAlignLast, TextAutospace, TextUnderlinePosition};
114pub use self::text::{
115 TextDecorationInset, TextDecorationLength, TextDecorationSkipInk, TextJustify, TextTransform,
116};
117pub use self::time::Time;
118pub use self::transform::{Rotate, Scale, Transform};
119pub use self::transform::{TransformBox, TransformOrigin, TransformStyle, Translate};
120#[cfg(feature = "gecko")]
121pub use self::ui::CursorImage;
122pub use self::ui::{
123 BoolInteger, Cursor, Inert, MozTheme, PointerEvents, ScrollbarColor, UserFocus, UserSelect,
124};
125pub use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
126
127pub mod align;
128pub mod angle;
129pub mod animation;
130pub mod background;
131pub mod basic_shape;
132pub mod border;
133#[path = "box.rs"]
134pub mod box_;
135pub mod calc;
136pub mod color;
137pub mod column;
138pub mod counters;
139pub mod easing;
140pub mod effects;
141pub mod flex;
142pub mod font;
143pub mod grid;
144pub mod image;
145pub mod intersection_observer;
146pub mod length;
147pub mod list;
148pub mod motion;
149pub mod outline;
150pub mod page;
151pub mod percentage;
152pub mod position;
153pub mod ratio;
154pub mod rect;
155pub mod resolution;
156pub mod source_size_list;
157pub mod svg;
158pub mod svg_path;
159pub mod table;
160pub mod text;
161pub mod time;
162pub mod transform;
163pub mod ui;
164pub mod url;
165
166#[allow(missing_docs)]
169#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
170pub enum AngleOrPercentage {
171 Percentage(Percentage),
172 Angle(Angle),
173}
174
175impl AngleOrPercentage {
176 fn parse_internal<'i, 't>(
177 context: &ParserContext,
178 input: &mut Parser<'i, 't>,
179 allow_unitless_zero: AllowUnitlessZeroAngle,
180 ) -> Result<Self, ParseError<'i>> {
181 if let Ok(per) = input.try_parse(|i| Percentage::parse(context, i)) {
182 return Ok(AngleOrPercentage::Percentage(per));
183 }
184
185 Angle::parse_internal(context, input, allow_unitless_zero).map(AngleOrPercentage::Angle)
186 }
187
188 pub fn parse_with_unitless<'i, 't>(
191 context: &ParserContext,
192 input: &mut Parser<'i, 't>,
193 ) -> Result<Self, ParseError<'i>> {
194 AngleOrPercentage::parse_internal(context, input, AllowUnitlessZeroAngle::Yes)
195 }
196}
197
198impl Parse for AngleOrPercentage {
199 fn parse<'i, 't>(
200 context: &ParserContext,
201 input: &mut Parser<'i, 't>,
202 ) -> Result<Self, ParseError<'i>> {
203 AngleOrPercentage::parse_internal(context, input, AllowUnitlessZeroAngle::No)
204 }
205}
206
207fn parse_number_with_clamping_mode<'i, 't>(
209 context: &ParserContext,
210 input: &mut Parser<'i, 't>,
211 clamping_mode: AllowedNumericType,
212) -> Result<Number, ParseError<'i>> {
213 let location = input.current_source_location();
214 match *input.next()? {
215 Token::Number { value, .. } if clamping_mode.is_ok(context.parsing_mode, value) => {
216 Ok(Number {
217 value,
218 calc_clamping_mode: None,
219 })
220 },
221 Token::Function(ref name) => {
222 let function = CalcNode::math_function(context, name, location)?;
223 let value = CalcNode::parse_number(context, input, function)?;
224 Ok(Number {
225 value,
226 calc_clamping_mode: Some(clamping_mode),
227 })
228 },
229 ref t => Err(location.new_unexpected_token_error(t.clone())),
230 }
231}
232
233#[derive(Clone, Copy, Debug, MallocSizeOf, PartialOrd, ToShmem)]
237pub struct Number {
238 value: CSSFloat,
240 calc_clamping_mode: Option<AllowedNumericType>,
243}
244
245impl Parse for Number {
246 fn parse<'i, 't>(
247 context: &ParserContext,
248 input: &mut Parser<'i, 't>,
249 ) -> Result<Self, ParseError<'i>> {
250 parse_number_with_clamping_mode(context, input, AllowedNumericType::All)
251 }
252}
253
254impl PartialEq<Number> for Number {
255 fn eq(&self, other: &Number) -> bool {
256 if self.calc_clamping_mode != other.calc_clamping_mode {
257 return false;
258 }
259
260 self.value == other.value || (self.value.is_nan() && other.value.is_nan())
261 }
262}
263
264impl Number {
265 #[inline]
267 fn new_with_clamping_mode(
268 value: CSSFloat,
269 calc_clamping_mode: Option<AllowedNumericType>,
270 ) -> Self {
271 Self {
272 value,
273 calc_clamping_mode,
274 }
275 }
276
277 pub fn to_percentage(&self) -> Percentage {
279 Percentage::new_with_clamping_mode(self.value, self.calc_clamping_mode)
280 }
281
282 #[inline]
284 pub fn new(val: CSSFloat) -> Self {
285 Self::new_with_clamping_mode(val, None)
286 }
287
288 #[inline]
290 pub fn was_calc(&self) -> bool {
291 self.calc_clamping_mode.is_some()
292 }
293
294 #[inline]
296 pub fn get(&self) -> f32 {
297 crate::values::normalize(
298 self.calc_clamping_mode
299 .map_or(self.value, |mode| mode.clamp(self.value)),
300 )
301 .min(f32::MAX)
302 .max(f32::MIN)
303 }
304
305 #[allow(missing_docs)]
306 pub fn parse_non_negative<'i, 't>(
307 context: &ParserContext,
308 input: &mut Parser<'i, 't>,
309 ) -> Result<Number, ParseError<'i>> {
310 parse_number_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
311 }
312
313 #[allow(missing_docs)]
314 pub fn parse_at_least_one<'i, 't>(
315 context: &ParserContext,
316 input: &mut Parser<'i, 't>,
317 ) -> Result<Number, ParseError<'i>> {
318 parse_number_with_clamping_mode(context, input, AllowedNumericType::AtLeastOne)
319 }
320
321 #[inline]
323 pub fn clamp_to_one(self) -> Self {
324 Number {
325 value: self.value.min(1.),
326 calc_clamping_mode: self.calc_clamping_mode,
327 }
328 }
329}
330
331impl ToComputedValue for Number {
332 type ComputedValue = CSSFloat;
333
334 #[inline]
335 fn to_computed_value(&self, _: &Context) -> CSSFloat {
336 self.get()
337 }
338
339 #[inline]
340 fn from_computed_value(computed: &CSSFloat) -> Self {
341 Number {
342 value: *computed,
343 calc_clamping_mode: None,
344 }
345 }
346}
347
348impl ToCss for Number {
349 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
350 where
351 W: Write,
352 {
353 serialize_number(self.value, self.calc_clamping_mode.is_some(), dest)
354 }
355}
356
357impl ToTyped for Number {
358 fn to_typed(&self) -> Option<TypedValue> {
359 let value = self.value;
360 let unit = CssString::from("number");
361 Some(TypedValue::Numeric(NumericValue::Unit { value, unit }))
362 }
363}
364
365impl IsParallelTo for (Number, Number, Number) {
366 fn is_parallel_to(&self, vector: &DirectionVector) -> bool {
367 use euclid::approxeq::ApproxEq;
368 let self_vector = DirectionVector::new(self.0.get(), self.1.get(), self.2.get());
371 self_vector
372 .cross(*vector)
373 .square_length()
374 .approx_eq(&0.0f32)
375 }
376}
377
378impl SpecifiedValueInfo for Number {}
379
380impl Add for Number {
381 type Output = Self;
382
383 fn add(self, other: Self) -> Self {
384 Self::new(self.get() + other.get())
385 }
386}
387
388impl Zero for Number {
389 #[inline]
390 fn zero() -> Self {
391 Self::new(0.)
392 }
393
394 #[inline]
395 fn is_zero(&self) -> bool {
396 self.get() == 0.
397 }
398}
399
400impl From<Number> for f32 {
401 #[inline]
402 fn from(n: Number) -> Self {
403 n.get()
404 }
405}
406
407impl From<Number> for f64 {
408 #[inline]
409 fn from(n: Number) -> Self {
410 n.get() as f64
411 }
412}
413
414pub type NonNegativeNumber = NonNegative<Number>;
416
417impl Parse for NonNegativeNumber {
418 fn parse<'i, 't>(
419 context: &ParserContext,
420 input: &mut Parser<'i, 't>,
421 ) -> Result<Self, ParseError<'i>> {
422 parse_number_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
423 .map(NonNegative::<Number>)
424 }
425}
426
427impl One for NonNegativeNumber {
428 #[inline]
429 fn one() -> Self {
430 NonNegativeNumber::new(1.0)
431 }
432
433 #[inline]
434 fn is_one(&self) -> bool {
435 self.get() == 1.0
436 }
437}
438
439impl NonNegativeNumber {
440 pub fn new(val: CSSFloat) -> Self {
442 NonNegative::<Number>(Number::new(val.max(0.)))
443 }
444
445 #[inline]
447 pub fn get(&self) -> f32 {
448 self.0.get()
449 }
450}
451
452pub type NonNegativeInteger = NonNegative<Integer>;
454
455impl Parse for NonNegativeInteger {
456 fn parse<'i, 't>(
457 context: &ParserContext,
458 input: &mut Parser<'i, 't>,
459 ) -> Result<Self, ParseError<'i>> {
460 Ok(NonNegative(Integer::parse_non_negative(context, input)?))
461 }
462}
463
464pub type GreaterThanOrEqualToOneNumber = GreaterThanOrEqualToOne<Number>;
466
467impl Parse for GreaterThanOrEqualToOneNumber {
468 fn parse<'i, 't>(
469 context: &ParserContext,
470 input: &mut Parser<'i, 't>,
471 ) -> Result<Self, ParseError<'i>> {
472 parse_number_with_clamping_mode(context, input, AllowedNumericType::AtLeastOne)
473 .map(GreaterThanOrEqualToOne::<Number>)
474 }
475}
476
477#[allow(missing_docs)]
481#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
482pub enum NumberOrPercentage {
483 Percentage(Percentage),
484 Number(Number),
485}
486
487impl NumberOrPercentage {
488 fn parse_with_clamping_mode<'i, 't>(
489 context: &ParserContext,
490 input: &mut Parser<'i, 't>,
491 type_: AllowedNumericType,
492 ) -> Result<Self, ParseError<'i>> {
493 if let Ok(per) =
494 input.try_parse(|i| Percentage::parse_with_clamping_mode(context, i, type_))
495 {
496 return Ok(NumberOrPercentage::Percentage(per));
497 }
498
499 parse_number_with_clamping_mode(context, input, type_).map(NumberOrPercentage::Number)
500 }
501
502 pub fn parse_non_negative<'i, 't>(
504 context: &ParserContext,
505 input: &mut Parser<'i, 't>,
506 ) -> Result<Self, ParseError<'i>> {
507 Self::parse_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
508 }
509
510 pub fn to_percentage(self) -> Percentage {
512 match self {
513 Self::Percentage(p) => p,
514 Self::Number(n) => n.to_percentage(),
515 }
516 }
517
518 pub fn to_number(self) -> Number {
520 match self {
521 Self::Percentage(p) => p.to_number(),
522 Self::Number(n) => n,
523 }
524 }
525}
526
527impl Parse for NumberOrPercentage {
528 fn parse<'i, 't>(
529 context: &ParserContext,
530 input: &mut Parser<'i, 't>,
531 ) -> Result<Self, ParseError<'i>> {
532 Self::parse_with_clamping_mode(context, input, AllowedNumericType::All)
533 }
534}
535
536pub type NonNegativeNumberOrPercentage = NonNegative<NumberOrPercentage>;
538
539impl NonNegativeNumberOrPercentage {
540 #[inline]
542 pub fn hundred_percent() -> Self {
543 NonNegative(NumberOrPercentage::Percentage(Percentage::hundred()))
544 }
545
546 #[inline]
548 pub fn new_number(n: f32) -> Self {
549 NonNegative(NumberOrPercentage::Number(Number::new(n)))
550 }
551}
552
553impl Parse for NonNegativeNumberOrPercentage {
554 fn parse<'i, 't>(
555 context: &ParserContext,
556 input: &mut Parser<'i, 't>,
557 ) -> Result<Self, ParseError<'i>> {
558 Ok(NonNegative(NumberOrPercentage::parse_non_negative(
559 context, input,
560 )?))
561 }
562}
563
564#[derive(
568 Clone,
569 Copy,
570 Debug,
571 MallocSizeOf,
572 PartialEq,
573 PartialOrd,
574 SpecifiedValueInfo,
575 ToCss,
576 ToShmem,
577 ToTyped,
578)]
579pub struct Opacity(Number);
580
581impl Parse for Opacity {
582 fn parse<'i, 't>(
586 context: &ParserContext,
587 input: &mut Parser<'i, 't>,
588 ) -> Result<Self, ParseError<'i>> {
589 let number = NumberOrPercentage::parse(context, input)?.to_number();
590 Ok(Opacity(number))
591 }
592}
593
594impl ToComputedValue for Opacity {
595 type ComputedValue = CSSFloat;
596
597 #[inline]
598 fn to_computed_value(&self, context: &Context) -> CSSFloat {
599 let value = self.0.to_computed_value(context);
600 if context.for_smil_animation {
601 value
604 } else {
605 value.min(1.0).max(0.0)
606 }
607 }
608
609 #[inline]
610 fn from_computed_value(computed: &CSSFloat) -> Self {
611 Opacity(Number::from_computed_value(computed))
612 }
613}
614
615#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToShmem, ToTyped)]
621pub enum Integer {
622 Literal(CSSInteger),
624 Calc(CSSFloat),
626}
627
628impl Zero for Integer {
629 #[inline]
630 fn zero() -> Self {
631 Self::new(0)
632 }
633
634 #[inline]
635 fn is_zero(&self) -> bool {
636 *self == 0
637 }
638}
639
640impl One for Integer {
641 #[inline]
642 fn one() -> Self {
643 Self::new(1)
644 }
645
646 #[inline]
647 fn is_one(&self) -> bool {
648 *self == 1
649 }
650}
651
652impl PartialEq<i32> for Integer {
653 fn eq(&self, value: &i32) -> bool {
654 self.value() == *value
655 }
656}
657
658impl Integer {
659 pub fn new(val: CSSInteger) -> Self {
661 Self::Literal(val)
662 }
663
664 pub fn value(&self) -> CSSInteger {
666 match *self {
667 Self::Literal(i) => i,
668 Self::Calc(n) => (n + 0.5).floor() as CSSInteger,
669 }
670 }
671
672 fn from_calc(val: CSSFloat) -> Self {
674 Self::Calc(val)
675 }
676}
677
678impl Parse for Integer {
679 fn parse<'i, 't>(
680 context: &ParserContext,
681 input: &mut Parser<'i, 't>,
682 ) -> Result<Self, ParseError<'i>> {
683 let location = input.current_source_location();
684 match *input.next()? {
685 Token::Number {
686 int_value: Some(v), ..
687 } => Ok(Integer::new(v)),
688 Token::Function(ref name) => {
689 let function = CalcNode::math_function(context, name, location)?;
690 let result = CalcNode::parse_number(context, input, function)?;
691 Ok(Integer::from_calc(result))
692 },
693 ref t => Err(location.new_unexpected_token_error(t.clone())),
694 }
695 }
696}
697
698impl Integer {
699 pub fn parse_with_minimum<'i, 't>(
701 context: &ParserContext,
702 input: &mut Parser<'i, 't>,
703 min: i32,
704 ) -> Result<Integer, ParseError<'i>> {
705 let value = Integer::parse(context, input)?;
706 if value.value() < min {
712 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
713 }
714 Ok(value)
715 }
716
717 pub fn parse_non_negative<'i, 't>(
719 context: &ParserContext,
720 input: &mut Parser<'i, 't>,
721 ) -> Result<Integer, ParseError<'i>> {
722 Integer::parse_with_minimum(context, input, 0)
723 }
724
725 pub fn parse_positive<'i, 't>(
727 context: &ParserContext,
728 input: &mut Parser<'i, 't>,
729 ) -> Result<Integer, ParseError<'i>> {
730 Integer::parse_with_minimum(context, input, 1)
731 }
732}
733
734impl ToComputedValue for Integer {
735 type ComputedValue = i32;
736
737 #[inline]
738 fn to_computed_value(&self, _: &Context) -> i32 {
739 self.value()
740 }
741
742 #[inline]
743 fn from_computed_value(computed: &i32) -> Self {
744 Integer::new(*computed)
745 }
746}
747
748impl ToCss for Integer {
749 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
750 where
751 W: Write,
752 {
753 match *self {
754 Integer::Literal(i) => i.to_css(dest),
755 Integer::Calc(n) => {
756 dest.write_str("calc(")?;
757 n.to_css(dest)?;
758 dest.write_char(')')
759 },
760 }
761 }
762}
763
764impl SpecifiedValueInfo for Integer {}
765
766pub type PositiveInteger = GreaterThanOrEqualToOne<Integer>;
768
769impl Parse for PositiveInteger {
770 #[inline]
771 fn parse<'i, 't>(
772 context: &ParserContext,
773 input: &mut Parser<'i, 't>,
774 ) -> Result<Self, ParseError<'i>> {
775 Integer::parse_positive(context, input).map(GreaterThanOrEqualToOne)
776 }
777}
778
779pub type TrackBreadth = GenericTrackBreadth<LengthPercentage>;
781
782pub type TrackSize = GenericTrackSize<LengthPercentage>;
784
785pub type ImplicitGridTracks = GenericImplicitGridTracks<TrackSize>;
787
788pub type TrackList = GenericTrackList<LengthPercentage, Integer>;
791
792pub type GridLine = GenericGridLine<Integer>;
794
795pub type GridTemplateComponent = GenericGridTemplateComponent<LengthPercentage, Integer>;
797
798pub type ClipRect = generics::GenericClipRect<LengthOrAuto>;
800
801impl Parse for ClipRect {
802 fn parse<'i, 't>(
803 context: &ParserContext,
804 input: &mut Parser<'i, 't>,
805 ) -> Result<Self, ParseError<'i>> {
806 Self::parse_quirky(context, input, AllowQuirks::No)
807 }
808}
809
810impl ClipRect {
811 fn parse_quirky<'i, 't>(
813 context: &ParserContext,
814 input: &mut Parser<'i, 't>,
815 allow_quirks: AllowQuirks,
816 ) -> Result<Self, ParseError<'i>> {
817 input.expect_function_matching("rect")?;
818
819 fn parse_argument<'i, 't>(
820 context: &ParserContext,
821 input: &mut Parser<'i, 't>,
822 allow_quirks: AllowQuirks,
823 ) -> Result<LengthOrAuto, ParseError<'i>> {
824 LengthOrAuto::parse_quirky(context, input, allow_quirks)
825 }
826
827 input.parse_nested_block(|input| {
828 let top = parse_argument(context, input, allow_quirks)?;
829 let right;
830 let bottom;
831 let left;
832
833 if input.try_parse(|input| input.expect_comma()).is_ok() {
834 right = parse_argument(context, input, allow_quirks)?;
835 input.expect_comma()?;
836 bottom = parse_argument(context, input, allow_quirks)?;
837 input.expect_comma()?;
838 left = parse_argument(context, input, allow_quirks)?;
839 } else {
840 right = parse_argument(context, input, allow_quirks)?;
841 bottom = parse_argument(context, input, allow_quirks)?;
842 left = parse_argument(context, input, allow_quirks)?;
843 }
844
845 Ok(ClipRect {
846 top,
847 right,
848 bottom,
849 left,
850 })
851 })
852 }
853}
854
855pub type ClipRectOrAuto = generics::GenericClipRectOrAuto<ClipRect>;
857
858impl ClipRectOrAuto {
859 pub fn parse_quirky<'i, 't>(
861 context: &ParserContext,
862 input: &mut Parser<'i, 't>,
863 allow_quirks: AllowQuirks,
864 ) -> Result<Self, ParseError<'i>> {
865 if let Ok(v) = input.try_parse(|i| ClipRect::parse_quirky(context, i, allow_quirks)) {
866 return Ok(generics::GenericClipRectOrAuto::Rect(v));
867 }
868 input.expect_ident_matching("auto")?;
869 Ok(generics::GenericClipRectOrAuto::Auto)
870 }
871}
872
873#[derive(Clone, Copy, PartialEq)]
875pub enum AllowQuirks {
876 No,
878 Yes,
880 Always,
882}
883
884impl AllowQuirks {
885 pub fn allowed(self, quirks_mode: QuirksMode) -> bool {
887 match self {
888 AllowQuirks::Always => true,
889 AllowQuirks::No => false,
890 AllowQuirks::Yes => quirks_mode == QuirksMode::Quirks,
891 }
892 }
893}
894
895#[derive(
899 Clone,
900 Debug,
901 Eq,
902 MallocSizeOf,
903 PartialEq,
904 SpecifiedValueInfo,
905 ToComputedValue,
906 ToResolvedValue,
907 ToShmem,
908)]
909#[css(function)]
910#[repr(C)]
911pub struct Attr {
912 pub namespace_prefix: Prefix,
914 pub namespace_url: Namespace,
916 pub attribute: Atom,
918 pub fallback: AtomString,
920}
921
922impl Parse for Attr {
923 fn parse<'i, 't>(
924 context: &ParserContext,
925 input: &mut Parser<'i, 't>,
926 ) -> Result<Attr, ParseError<'i>> {
927 input.expect_function_matching("attr")?;
928 input.parse_nested_block(|i| Attr::parse_function(context, i))
929 }
930}
931
932fn get_namespace_for_prefix(prefix: &Prefix, context: &ParserContext) -> Option<Namespace> {
934 context.namespaces.prefixes.get(prefix).cloned()
935}
936
937fn parse_namespace<'i, 't>(
939 context: &ParserContext,
940 input: &mut Parser<'i, 't>,
941) -> Result<(Prefix, Namespace), ParseError<'i>> {
942 let ns_prefix = match input.next()? {
943 Token::Ident(ref prefix) => Some(Prefix::from(prefix.as_ref())),
944 Token::Delim('|') => None,
945 _ => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
946 };
947
948 if ns_prefix.is_some() && !matches!(*input.next_including_whitespace()?, Token::Delim('|')) {
949 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
950 }
951
952 if let Some(prefix) = ns_prefix {
953 let ns = match get_namespace_for_prefix(&prefix, context) {
954 Some(ns) => ns,
955 None => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
956 };
957 Ok((prefix, ns))
958 } else {
959 Ok((Prefix::default(), Namespace::default()))
960 }
961}
962
963impl Attr {
964 pub fn parse_function<'i, 't>(
967 context: &ParserContext,
968 input: &mut Parser<'i, 't>,
969 ) -> Result<Attr, ParseError<'i>> {
970 let namespace = input
972 .try_parse(|input| parse_namespace(context, input))
973 .ok();
974 let namespace_is_some = namespace.is_some();
975 let (namespace_prefix, namespace_url) = namespace.unwrap_or_default();
976
977 let attribute = Atom::from(if namespace_is_some {
979 let location = input.current_source_location();
980 match *input.next_including_whitespace()? {
981 Token::Ident(ref ident) => ident.as_ref(),
982 ref t => return Err(location.new_unexpected_token_error(t.clone())),
983 }
984 } else {
985 input.expect_ident()?.as_ref()
986 });
987
988 let fallback = input
991 .try_parse(|input| -> Result<AtomString, ParseError<'i>> {
992 input.expect_comma()?;
993 Ok(input.expect_string()?.as_ref().into())
994 })
995 .unwrap_or_default();
996
997 Ok(Attr {
998 namespace_prefix,
999 namespace_url,
1000 attribute,
1001 fallback,
1002 })
1003 }
1004}
1005
1006impl ToCss for Attr {
1007 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1008 where
1009 W: Write,
1010 {
1011 dest.write_str("attr(")?;
1012 if !self.namespace_prefix.is_empty() {
1013 serialize_atom_identifier(&self.namespace_prefix, dest)?;
1014 dest.write_char('|')?;
1015 }
1016 serialize_atom_identifier(&self.attribute, dest)?;
1017
1018 if !self.fallback.is_empty() {
1019 dest.write_str(", ")?;
1020 self.fallback.to_css(dest)?;
1021 }
1022
1023 dest.write_char(')')
1024 }
1025}