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