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,
63 FontSynthesisStyle,
64};
65pub use self::font::{FontVariantAlternates, FontWeight};
66pub use self::font::{FontVariantEastAsian, FontVariationSettings, LineHeight};
67pub use self::font::{MathDepth, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextScale};
68pub use self::image::{EndingShape as GradientEndingShape, Gradient, Image, ImageRendering};
69pub use self::length::{AbsoluteLength, CalcLengthPercentage, CharacterWidth};
70pub use self::length::{FontRelativeLength, Length, LengthOrNumber, NonNegativeLengthOrNumber};
71pub use self::length::{LengthOrAuto, LengthPercentage, LengthPercentageOrAuto};
72pub use self::length::{Margin, MaxSize, Size};
73pub use self::length::{NoCalcLength, ViewportPercentageLength, ViewportVariant};
74pub use self::length::{
75 NonNegativeLength, NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto,
76};
77#[cfg(feature = "gecko")]
78pub use self::list::ListStyleType;
79pub use self::list::Quotes;
80pub use self::motion::{OffsetPath, OffsetPosition, OffsetRotate};
81pub use self::outline::OutlineStyle;
82pub use self::page::{PageName, PageOrientation, PageSize, PageSizeOrientation, PaperSize};
83pub use self::percentage::{NonNegativePercentage, Percentage};
84pub use self::position::AnchorFunction;
85pub use self::position::AnchorName;
86pub use self::position::AnchorScope;
87pub use self::position::AspectRatio;
88pub use self::position::Inset;
89pub use self::position::PositionAnchor;
90pub use self::position::PositionTryFallbacks;
91pub use self::position::PositionTryOrder;
92pub use self::position::PositionVisibility;
93pub use self::position::{GridAutoFlow, GridTemplateAreas, Position, PositionOrAuto};
94pub use self::position::{MasonryAutoFlow, MasonryItemOrder, MasonryPlacement};
95pub use self::position::{PositionArea, PositionAreaKeyword};
96pub use self::position::{PositionComponent, ZIndex};
97pub use self::ratio::Ratio;
98pub use self::rect::NonNegativeLengthOrNumberRect;
99pub use self::resolution::Resolution;
100pub use self::svg::{DProperty, MozContextProperties};
101pub use self::svg::{SVGLength, SVGOpacity, SVGPaint};
102pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth, VectorEffect};
103pub use self::svg_path::SVGPathData;
104pub use self::text::RubyPosition;
105pub use self::text::{HyphenateCharacter, HyphenateLimitChars};
106pub use self::text::{InitialLetter, LetterSpacing, LineBreak, TextAlign, TextIndent};
107pub use self::text::{OverflowWrap, TextEmphasisPosition, TextEmphasisStyle, WordBreak};
108pub use self::text::{TextAlignKeyword, TextDecorationLine, TextOverflow, WordSpacing};
109pub use self::text::{TextAlignLast, TextAutospace, TextUnderlinePosition};
110pub use self::text::{
111 TextDecorationLength, TextDecorationSkipInk, TextDecorationTrim, TextJustify, TextTransform,
112};
113pub use self::time::Time;
114pub use self::transform::{Rotate, Scale, Transform};
115pub use self::transform::{TransformBox, TransformOrigin, TransformStyle, Translate};
116#[cfg(feature = "gecko")]
117pub use self::ui::CursorImage;
118pub use self::ui::{
119 BoolInteger, Cursor, Inert, MozTheme, PointerEvents, ScrollbarColor, UserFocus, UserSelect,
120};
121pub use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
122
123pub mod align;
124pub mod angle;
125pub mod animation;
126pub mod background;
127pub mod basic_shape;
128pub mod border;
129#[path = "box.rs"]
130pub mod box_;
131pub mod calc;
132pub mod color;
133pub mod column;
134pub mod counters;
135pub mod easing;
136pub mod effects;
137pub mod flex;
138pub mod font;
139pub mod grid;
140pub mod image;
141pub mod intersection_observer;
142pub mod length;
143pub mod list;
144pub mod motion;
145pub mod outline;
146pub mod page;
147pub mod percentage;
148pub mod position;
149pub mod ratio;
150pub mod rect;
151pub mod resolution;
152pub mod source_size_list;
153pub mod svg;
154pub mod svg_path;
155pub mod table;
156pub mod text;
157pub mod time;
158pub mod transform;
159pub mod ui;
160pub mod url;
161
162#[allow(missing_docs)]
165#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
166pub enum AngleOrPercentage {
167 Percentage(Percentage),
168 Angle(Angle),
169}
170
171impl AngleOrPercentage {
172 fn parse_internal<'i, 't>(
173 context: &ParserContext,
174 input: &mut Parser<'i, 't>,
175 allow_unitless_zero: AllowUnitlessZeroAngle,
176 ) -> Result<Self, ParseError<'i>> {
177 if let Ok(per) = input.try_parse(|i| Percentage::parse(context, i)) {
178 return Ok(AngleOrPercentage::Percentage(per));
179 }
180
181 Angle::parse_internal(context, input, allow_unitless_zero).map(AngleOrPercentage::Angle)
182 }
183
184 pub fn parse_with_unitless<'i, 't>(
187 context: &ParserContext,
188 input: &mut Parser<'i, 't>,
189 ) -> Result<Self, ParseError<'i>> {
190 AngleOrPercentage::parse_internal(context, input, AllowUnitlessZeroAngle::Yes)
191 }
192}
193
194impl Parse for AngleOrPercentage {
195 fn parse<'i, 't>(
196 context: &ParserContext,
197 input: &mut Parser<'i, 't>,
198 ) -> Result<Self, ParseError<'i>> {
199 AngleOrPercentage::parse_internal(context, input, AllowUnitlessZeroAngle::No)
200 }
201}
202
203fn parse_number_with_clamping_mode<'i, 't>(
205 context: &ParserContext,
206 input: &mut Parser<'i, 't>,
207 clamping_mode: AllowedNumericType,
208) -> Result<Number, ParseError<'i>> {
209 let location = input.current_source_location();
210 match *input.next()? {
211 Token::Number { value, .. } if clamping_mode.is_ok(context.parsing_mode, value) => {
212 Ok(Number {
213 value,
214 calc_clamping_mode: None,
215 })
216 },
217 Token::Function(ref name) => {
218 let function = CalcNode::math_function(context, name, location)?;
219 let value = CalcNode::parse_number(context, input, function)?;
220 Ok(Number {
221 value,
222 calc_clamping_mode: Some(clamping_mode),
223 })
224 },
225 ref t => Err(location.new_unexpected_token_error(t.clone())),
226 }
227}
228
229#[derive(Clone, Copy, Debug, MallocSizeOf, PartialOrd, ToShmem)]
233pub struct Number {
234 value: CSSFloat,
236 calc_clamping_mode: Option<AllowedNumericType>,
239}
240
241impl Parse for Number {
242 fn parse<'i, 't>(
243 context: &ParserContext,
244 input: &mut Parser<'i, 't>,
245 ) -> Result<Self, ParseError<'i>> {
246 parse_number_with_clamping_mode(context, input, AllowedNumericType::All)
247 }
248}
249
250impl PartialEq<Number> for Number {
251 fn eq(&self, other: &Number) -> bool {
252 if self.calc_clamping_mode != other.calc_clamping_mode {
253 return false;
254 }
255
256 self.value == other.value || (self.value.is_nan() && other.value.is_nan())
257 }
258}
259
260impl Number {
261 #[inline]
263 fn new_with_clamping_mode(
264 value: CSSFloat,
265 calc_clamping_mode: Option<AllowedNumericType>,
266 ) -> Self {
267 Self {
268 value,
269 calc_clamping_mode,
270 }
271 }
272
273 pub fn to_percentage(&self) -> Percentage {
275 Percentage::new_with_clamping_mode(self.value, self.calc_clamping_mode)
276 }
277
278 #[inline]
280 pub fn new(val: CSSFloat) -> Self {
281 Self::new_with_clamping_mode(val, None)
282 }
283
284 #[inline]
286 pub fn was_calc(&self) -> bool {
287 self.calc_clamping_mode.is_some()
288 }
289
290 #[inline]
292 pub fn get(&self) -> f32 {
293 crate::values::normalize(
294 self.calc_clamping_mode
295 .map_or(self.value, |mode| mode.clamp(self.value)),
296 )
297 .min(f32::MAX)
298 .max(f32::MIN)
299 }
300
301 #[allow(missing_docs)]
302 pub fn parse_non_negative<'i, 't>(
303 context: &ParserContext,
304 input: &mut Parser<'i, 't>,
305 ) -> Result<Number, ParseError<'i>> {
306 parse_number_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
307 }
308
309 #[allow(missing_docs)]
310 pub fn parse_at_least_one<'i, 't>(
311 context: &ParserContext,
312 input: &mut Parser<'i, 't>,
313 ) -> Result<Number, ParseError<'i>> {
314 parse_number_with_clamping_mode(context, input, AllowedNumericType::AtLeastOne)
315 }
316
317 #[inline]
319 pub fn clamp_to_one(self) -> Self {
320 Number {
321 value: self.value.min(1.),
322 calc_clamping_mode: self.calc_clamping_mode,
323 }
324 }
325}
326
327impl ToComputedValue for Number {
328 type ComputedValue = CSSFloat;
329
330 #[inline]
331 fn to_computed_value(&self, _: &Context) -> CSSFloat {
332 self.get()
333 }
334
335 #[inline]
336 fn from_computed_value(computed: &CSSFloat) -> Self {
337 Number {
338 value: *computed,
339 calc_clamping_mode: None,
340 }
341 }
342}
343
344impl ToCss for Number {
345 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
346 where
347 W: Write,
348 {
349 serialize_number(self.value, self.calc_clamping_mode.is_some(), dest)
350 }
351}
352
353impl IsParallelTo for (Number, Number, Number) {
354 fn is_parallel_to(&self, vector: &DirectionVector) -> bool {
355 use euclid::approxeq::ApproxEq;
356 let self_vector = DirectionVector::new(self.0.get(), self.1.get(), self.2.get());
359 self_vector
360 .cross(*vector)
361 .square_length()
362 .approx_eq(&0.0f32)
363 }
364}
365
366impl SpecifiedValueInfo for Number {}
367
368impl Add for Number {
369 type Output = Self;
370
371 fn add(self, other: Self) -> Self {
372 Self::new(self.get() + other.get())
373 }
374}
375
376impl Zero for Number {
377 #[inline]
378 fn zero() -> Self {
379 Self::new(0.)
380 }
381
382 #[inline]
383 fn is_zero(&self) -> bool {
384 self.get() == 0.
385 }
386}
387
388impl From<Number> for f32 {
389 #[inline]
390 fn from(n: Number) -> Self {
391 n.get()
392 }
393}
394
395impl From<Number> for f64 {
396 #[inline]
397 fn from(n: Number) -> Self {
398 n.get() as f64
399 }
400}
401
402pub type NonNegativeNumber = NonNegative<Number>;
404
405impl Parse for NonNegativeNumber {
406 fn parse<'i, 't>(
407 context: &ParserContext,
408 input: &mut Parser<'i, 't>,
409 ) -> Result<Self, ParseError<'i>> {
410 parse_number_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
411 .map(NonNegative::<Number>)
412 }
413}
414
415impl One for NonNegativeNumber {
416 #[inline]
417 fn one() -> Self {
418 NonNegativeNumber::new(1.0)
419 }
420
421 #[inline]
422 fn is_one(&self) -> bool {
423 self.get() == 1.0
424 }
425}
426
427impl NonNegativeNumber {
428 pub fn new(val: CSSFloat) -> Self {
430 NonNegative::<Number>(Number::new(val.max(0.)))
431 }
432
433 #[inline]
435 pub fn get(&self) -> f32 {
436 self.0.get()
437 }
438}
439
440pub type NonNegativeInteger = NonNegative<Integer>;
442
443impl Parse for NonNegativeInteger {
444 fn parse<'i, 't>(
445 context: &ParserContext,
446 input: &mut Parser<'i, 't>,
447 ) -> Result<Self, ParseError<'i>> {
448 Ok(NonNegative(Integer::parse_non_negative(context, input)?))
449 }
450}
451
452pub type GreaterThanOrEqualToOneNumber = GreaterThanOrEqualToOne<Number>;
454
455impl Parse for GreaterThanOrEqualToOneNumber {
456 fn parse<'i, 't>(
457 context: &ParserContext,
458 input: &mut Parser<'i, 't>,
459 ) -> Result<Self, ParseError<'i>> {
460 parse_number_with_clamping_mode(context, input, AllowedNumericType::AtLeastOne)
461 .map(GreaterThanOrEqualToOne::<Number>)
462 }
463}
464
465#[allow(missing_docs)]
469#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
470pub enum NumberOrPercentage {
471 Percentage(Percentage),
472 Number(Number),
473}
474
475impl NumberOrPercentage {
476 fn parse_with_clamping_mode<'i, 't>(
477 context: &ParserContext,
478 input: &mut Parser<'i, 't>,
479 type_: AllowedNumericType,
480 ) -> Result<Self, ParseError<'i>> {
481 if let Ok(per) =
482 input.try_parse(|i| Percentage::parse_with_clamping_mode(context, i, type_))
483 {
484 return Ok(NumberOrPercentage::Percentage(per));
485 }
486
487 parse_number_with_clamping_mode(context, input, type_).map(NumberOrPercentage::Number)
488 }
489
490 pub fn parse_non_negative<'i, 't>(
492 context: &ParserContext,
493 input: &mut Parser<'i, 't>,
494 ) -> Result<Self, ParseError<'i>> {
495 Self::parse_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
496 }
497
498 pub fn to_percentage(self) -> Percentage {
500 match self {
501 Self::Percentage(p) => p,
502 Self::Number(n) => n.to_percentage(),
503 }
504 }
505
506 pub fn to_number(self) -> Number {
508 match self {
509 Self::Percentage(p) => p.to_number(),
510 Self::Number(n) => n,
511 }
512 }
513}
514
515impl Parse for NumberOrPercentage {
516 fn parse<'i, 't>(
517 context: &ParserContext,
518 input: &mut Parser<'i, 't>,
519 ) -> Result<Self, ParseError<'i>> {
520 Self::parse_with_clamping_mode(context, input, AllowedNumericType::All)
521 }
522}
523
524pub type NonNegativeNumberOrPercentage = NonNegative<NumberOrPercentage>;
526
527impl NonNegativeNumberOrPercentage {
528 #[inline]
530 pub fn hundred_percent() -> Self {
531 NonNegative(NumberOrPercentage::Percentage(Percentage::hundred()))
532 }
533
534 #[inline]
536 pub fn new_number(n: f32) -> Self {
537 NonNegative(NumberOrPercentage::Number(Number::new(n)))
538 }
539}
540
541impl Parse for NonNegativeNumberOrPercentage {
542 fn parse<'i, 't>(
543 context: &ParserContext,
544 input: &mut Parser<'i, 't>,
545 ) -> Result<Self, ParseError<'i>> {
546 Ok(NonNegative(NumberOrPercentage::parse_non_negative(
547 context, input,
548 )?))
549 }
550}
551
552#[derive(
556 Clone,
557 Copy,
558 Debug,
559 MallocSizeOf,
560 PartialEq,
561 PartialOrd,
562 SpecifiedValueInfo,
563 ToCss,
564 ToShmem,
565 ToTyped,
566)]
567pub struct Opacity(Number);
568
569impl Parse for Opacity {
570 fn parse<'i, 't>(
574 context: &ParserContext,
575 input: &mut Parser<'i, 't>,
576 ) -> Result<Self, ParseError<'i>> {
577 let number = NumberOrPercentage::parse(context, input)?.to_number();
578 Ok(Opacity(number))
579 }
580}
581
582impl ToComputedValue for Opacity {
583 type ComputedValue = CSSFloat;
584
585 #[inline]
586 fn to_computed_value(&self, context: &Context) -> CSSFloat {
587 let value = self.0.to_computed_value(context);
588 if context.for_smil_animation {
589 value
592 } else {
593 value.min(1.0).max(0.0)
594 }
595 }
596
597 #[inline]
598 fn from_computed_value(computed: &CSSFloat) -> Self {
599 Opacity(Number::from_computed_value(computed))
600 }
601}
602
603#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToShmem, ToTyped)]
609pub enum Integer {
610 Literal(CSSInteger),
612 Calc(CSSFloat),
614}
615
616impl Zero for Integer {
617 #[inline]
618 fn zero() -> Self {
619 Self::new(0)
620 }
621
622 #[inline]
623 fn is_zero(&self) -> bool {
624 *self == 0
625 }
626}
627
628impl One for Integer {
629 #[inline]
630 fn one() -> Self {
631 Self::new(1)
632 }
633
634 #[inline]
635 fn is_one(&self) -> bool {
636 *self == 1
637 }
638}
639
640impl PartialEq<i32> for Integer {
641 fn eq(&self, value: &i32) -> bool {
642 self.value() == *value
643 }
644}
645
646impl Integer {
647 pub fn new(val: CSSInteger) -> Self {
649 Self::Literal(val)
650 }
651
652 pub fn value(&self) -> CSSInteger {
654 match *self {
655 Self::Literal(i) => i,
656 Self::Calc(n) => (n + 0.5).floor() as CSSInteger,
657 }
658 }
659
660 fn from_calc(val: CSSFloat) -> Self {
662 Self::Calc(val)
663 }
664}
665
666impl Parse for Integer {
667 fn parse<'i, 't>(
668 context: &ParserContext,
669 input: &mut Parser<'i, 't>,
670 ) -> Result<Self, ParseError<'i>> {
671 let location = input.current_source_location();
672 match *input.next()? {
673 Token::Number {
674 int_value: Some(v), ..
675 } => Ok(Integer::new(v)),
676 Token::Function(ref name) => {
677 let function = CalcNode::math_function(context, name, location)?;
678 let result = CalcNode::parse_number(context, input, function)?;
679 Ok(Integer::from_calc(result))
680 },
681 ref t => Err(location.new_unexpected_token_error(t.clone())),
682 }
683 }
684}
685
686impl Integer {
687 pub fn parse_with_minimum<'i, 't>(
689 context: &ParserContext,
690 input: &mut Parser<'i, 't>,
691 min: i32,
692 ) -> Result<Integer, ParseError<'i>> {
693 let value = Integer::parse(context, input)?;
694 if value.value() < min {
700 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
701 }
702 Ok(value)
703 }
704
705 pub fn parse_non_negative<'i, 't>(
707 context: &ParserContext,
708 input: &mut Parser<'i, 't>,
709 ) -> Result<Integer, ParseError<'i>> {
710 Integer::parse_with_minimum(context, input, 0)
711 }
712
713 pub fn parse_positive<'i, 't>(
715 context: &ParserContext,
716 input: &mut Parser<'i, 't>,
717 ) -> Result<Integer, ParseError<'i>> {
718 Integer::parse_with_minimum(context, input, 1)
719 }
720}
721
722impl ToComputedValue for Integer {
723 type ComputedValue = i32;
724
725 #[inline]
726 fn to_computed_value(&self, _: &Context) -> i32 {
727 self.value()
728 }
729
730 #[inline]
731 fn from_computed_value(computed: &i32) -> Self {
732 Integer::new(*computed)
733 }
734}
735
736impl ToCss for Integer {
737 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
738 where
739 W: Write,
740 {
741 match *self {
742 Integer::Literal(i) => i.to_css(dest),
743 Integer::Calc(n) => {
744 dest.write_str("calc(")?;
745 n.to_css(dest)?;
746 dest.write_char(')')
747 },
748 }
749 }
750}
751
752impl SpecifiedValueInfo for Integer {}
753
754pub type PositiveInteger = GreaterThanOrEqualToOne<Integer>;
756
757impl Parse for PositiveInteger {
758 #[inline]
759 fn parse<'i, 't>(
760 context: &ParserContext,
761 input: &mut Parser<'i, 't>,
762 ) -> Result<Self, ParseError<'i>> {
763 Integer::parse_positive(context, input).map(GreaterThanOrEqualToOne)
764 }
765}
766
767pub type TrackBreadth = GenericTrackBreadth<LengthPercentage>;
769
770pub type TrackSize = GenericTrackSize<LengthPercentage>;
772
773pub type ImplicitGridTracks = GenericImplicitGridTracks<TrackSize>;
775
776pub type TrackList = GenericTrackList<LengthPercentage, Integer>;
779
780pub type GridLine = GenericGridLine<Integer>;
782
783pub type GridTemplateComponent = GenericGridTemplateComponent<LengthPercentage, Integer>;
785
786pub type ClipRect = generics::GenericClipRect<LengthOrAuto>;
788
789impl Parse for ClipRect {
790 fn parse<'i, 't>(
791 context: &ParserContext,
792 input: &mut Parser<'i, 't>,
793 ) -> Result<Self, ParseError<'i>> {
794 Self::parse_quirky(context, input, AllowQuirks::No)
795 }
796}
797
798impl ClipRect {
799 fn parse_quirky<'i, 't>(
801 context: &ParserContext,
802 input: &mut Parser<'i, 't>,
803 allow_quirks: AllowQuirks,
804 ) -> Result<Self, ParseError<'i>> {
805 input.expect_function_matching("rect")?;
806
807 fn parse_argument<'i, 't>(
808 context: &ParserContext,
809 input: &mut Parser<'i, 't>,
810 allow_quirks: AllowQuirks,
811 ) -> Result<LengthOrAuto, ParseError<'i>> {
812 LengthOrAuto::parse_quirky(context, input, allow_quirks)
813 }
814
815 input.parse_nested_block(|input| {
816 let top = parse_argument(context, input, allow_quirks)?;
817 let right;
818 let bottom;
819 let left;
820
821 if input.try_parse(|input| input.expect_comma()).is_ok() {
822 right = parse_argument(context, input, allow_quirks)?;
823 input.expect_comma()?;
824 bottom = parse_argument(context, input, allow_quirks)?;
825 input.expect_comma()?;
826 left = parse_argument(context, input, allow_quirks)?;
827 } else {
828 right = parse_argument(context, input, allow_quirks)?;
829 bottom = parse_argument(context, input, allow_quirks)?;
830 left = parse_argument(context, input, allow_quirks)?;
831 }
832
833 Ok(ClipRect {
834 top,
835 right,
836 bottom,
837 left,
838 })
839 })
840 }
841}
842
843pub type ClipRectOrAuto = generics::GenericClipRectOrAuto<ClipRect>;
845
846impl ClipRectOrAuto {
847 pub fn parse_quirky<'i, 't>(
849 context: &ParserContext,
850 input: &mut Parser<'i, 't>,
851 allow_quirks: AllowQuirks,
852 ) -> Result<Self, ParseError<'i>> {
853 if let Ok(v) = input.try_parse(|i| ClipRect::parse_quirky(context, i, allow_quirks)) {
854 return Ok(generics::GenericClipRectOrAuto::Rect(v));
855 }
856 input.expect_ident_matching("auto")?;
857 Ok(generics::GenericClipRectOrAuto::Auto)
858 }
859}
860
861#[derive(Clone, Copy, PartialEq)]
863pub enum AllowQuirks {
864 No,
866 Yes,
868 Always,
870}
871
872impl AllowQuirks {
873 pub fn allowed(self, quirks_mode: QuirksMode) -> bool {
875 match self {
876 AllowQuirks::Always => true,
877 AllowQuirks::No => false,
878 AllowQuirks::Yes => quirks_mode == QuirksMode::Quirks,
879 }
880 }
881}
882
883#[derive(
887 Clone,
888 Debug,
889 Eq,
890 MallocSizeOf,
891 PartialEq,
892 SpecifiedValueInfo,
893 ToComputedValue,
894 ToResolvedValue,
895 ToShmem,
896)]
897#[css(function)]
898#[repr(C)]
899pub struct Attr {
900 pub namespace_prefix: Prefix,
902 pub namespace_url: Namespace,
904 pub attribute: Atom,
906 pub fallback: AtomString,
908}
909
910impl Parse for Attr {
911 fn parse<'i, 't>(
912 context: &ParserContext,
913 input: &mut Parser<'i, 't>,
914 ) -> Result<Attr, ParseError<'i>> {
915 input.expect_function_matching("attr")?;
916 input.parse_nested_block(|i| Attr::parse_function(context, i))
917 }
918}
919
920fn get_namespace_for_prefix(prefix: &Prefix, context: &ParserContext) -> Option<Namespace> {
922 context.namespaces.prefixes.get(prefix).cloned()
923}
924
925fn parse_namespace<'i, 't>(
927 context: &ParserContext,
928 input: &mut Parser<'i, 't>,
929) -> Result<(Prefix, Namespace), ParseError<'i>> {
930 let ns_prefix = match input.next()? {
931 Token::Ident(ref prefix) => Some(Prefix::from(prefix.as_ref())),
932 Token::Delim('|') => None,
933 _ => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
934 };
935
936 if ns_prefix.is_some() && !matches!(*input.next_including_whitespace()?, Token::Delim('|')) {
937 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
938 }
939
940 if let Some(prefix) = ns_prefix {
941 let ns = match get_namespace_for_prefix(&prefix, context) {
942 Some(ns) => ns,
943 None => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
944 };
945 Ok((prefix, ns))
946 } else {
947 Ok((Prefix::default(), Namespace::default()))
948 }
949}
950
951impl Attr {
952 pub fn parse_function<'i, 't>(
955 context: &ParserContext,
956 input: &mut Parser<'i, 't>,
957 ) -> Result<Attr, ParseError<'i>> {
958 let namespace = input
960 .try_parse(|input| parse_namespace(context, input))
961 .ok();
962 let namespace_is_some = namespace.is_some();
963 let (namespace_prefix, namespace_url) = namespace.unwrap_or_default();
964
965 let attribute = Atom::from(if namespace_is_some {
967 let location = input.current_source_location();
968 match *input.next_including_whitespace()? {
969 Token::Ident(ref ident) => ident.as_ref(),
970 ref t => return Err(location.new_unexpected_token_error(t.clone())),
971 }
972 } else {
973 input.expect_ident()?.as_ref()
974 });
975
976 let fallback = input
979 .try_parse(|input| -> Result<AtomString, ParseError<'i>> {
980 input.expect_comma()?;
981 Ok(input.expect_string()?.as_ref().into())
982 })
983 .unwrap_or_default();
984
985 Ok(Attr {
986 namespace_prefix,
987 namespace_url,
988 attribute,
989 fallback,
990 })
991 }
992}
993
994impl ToCss for Attr {
995 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
996 where
997 W: Write,
998 {
999 dest.write_str("attr(")?;
1000 if !self.namespace_prefix.is_empty() {
1001 serialize_atom_identifier(&self.namespace_prefix, dest)?;
1002 dest.write_char('|')?;
1003 }
1004 serialize_atom_identifier(&self.attribute, dest)?;
1005
1006 if !self.fallback.is_empty() {
1007 dest.write_str(", ")?;
1008 self.fallback.to_css(dest)?;
1009 }
1010
1011 dest.write_char(')')
1012 }
1013}