1use super::computed::{Context, ToComputedValue};
10use super::generics::grid::ImplicitGridTracks as GenericImplicitGridTracks;
11use super::generics::grid::{GridLine as GenericGridLine, TrackBreadth as GenericTrackBreadth};
12use super::generics::grid::{TrackList as GenericTrackList, TrackSize as GenericTrackSize};
13use super::generics::{self, NonNegative};
14use super::CSSFloat;
15use crate::context::QuirksMode;
16use crate::derives::*;
17use crate::parser::{Parse, ParserContext};
18use crate::values::specified::number::parse_number_with_clamping_mode;
19use crate::values::{computed, serialize_atom_identifier, AtomString};
20use crate::{Atom, Namespace, Prefix};
21use cssparser::{Parser, Token};
22use rustc_hash::FxHashMap;
23use std::fmt::{self, Write};
24use style_traits::values::specified::AllowedNumericType;
25use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
26
27pub use self::align::{ContentDistribution, ItemPlacement, JustifyItems, SelfAlignment};
28pub use self::angle::{AllowUnitlessZeroAngle, Angle, NoCalcAngle};
29pub use self::animation::{
30 AnimationComposition, AnimationDirection, AnimationDuration, AnimationFillMode,
31 AnimationIterationCount, AnimationName, AnimationPlayState, AnimationRangeEnd,
32 AnimationRangeStart, AnimationTimeline, ScrollAxis, TimelineName, TransitionBehavior,
33 TransitionProperty, ViewTimelineInset, ViewTransitionClass, ViewTransitionName,
34};
35pub use self::background::{BackgroundClip, BackgroundRepeat, BackgroundSize};
36pub use self::basic_shape::FillRule;
37pub use self::border::{
38 BorderCornerRadius, BorderImageRepeat, BorderImageSideWidth, BorderImageSlice,
39 BorderImageWidth, BorderRadius, BorderSideOffset, BorderSideWidth, BorderSpacing, BorderStyle,
40 LineWidth,
41};
42pub use self::box_::{
43 AlignmentBaseline, Appearance, BaselineShift, BaselineSource, BreakBetween, BreakWithin, Clear,
44 Contain, ContainIntrinsicSize, ContainerName, ContainerType, ContentVisibility, Display,
45 DominantBaseline, Float, LineClamp, Overflow, OverflowAnchor, OverflowClipMargin,
46 OverscrollBehavior, Perspective, PositionProperty, Resize, ScrollSnapAlign, ScrollSnapAxis,
47 ScrollSnapStop, ScrollSnapStrictness, ScrollSnapType, ScrollbarGutter, TouchAction, WillChange,
48 WillChangeBits, WritingModeProperty, Zoom,
49};
50pub use self::calc::{CalcLengthPercentage, CalcNumeric};
51pub use self::color::{
52 Color, ColorOrAuto, ColorPropertyValue, ColorScheme, ForcedColorAdjust, PrintColorAdjust,
53};
54pub use self::column::ColumnCount;
55pub use self::corner_shape::{CornerShape, CornerShapeRect, SuperellipseArg};
56pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset, CounterSet};
57pub use self::easing::TimingFunction;
58pub use self::effects::{BoxShadow, Filter, SimpleShadow};
59pub use self::flex::FlexBasis;
60pub use self::font::{FontFamily, FontLanguageOverride, FontPalette, FontStyle};
61pub use self::font::{FontFeatureSettings, FontVariantLigatures, FontVariantNumeric};
62pub use self::font::{
63 FontSize, FontSizeAdjust, FontSizeAdjustFactor, FontSizeKeyword, FontStretch, FontSynthesis,
64 FontSynthesisStyle,
65};
66pub use self::font::{FontVariantAlternates, FontWeight};
67pub use self::font::{FontVariantEastAsian, FontVariationSettings, LineHeight};
68pub use self::font::{MathDepth, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextScale};
69pub use self::image::{
70 EndingShape as GradientEndingShape, Gradient, Image, ImageDecoding, ImageRendering,
71};
72pub use self::length::{Length, LengthOrNumber, LengthUnit, NonNegativeLengthOrNumber};
73pub use self::length::{LengthOrAuto, LengthPercentage, LengthPercentageOrAuto};
74pub use self::length::{Margin, MaxSize, Size};
75pub use self::length::{NoCalcLength, ViewportVariant};
76pub use self::length::{
77 NonNegativeLength, NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto,
78};
79pub use self::list::ListStyleType;
80pub use self::list::Quotes;
81pub use self::motion::{OffsetPath, OffsetPosition, OffsetRotate};
82pub use self::number::{
83 GreaterThanOrEqualToOneNumber, Integer, NoCalcNumber, NonNegativeInteger, NonNegativeNumber,
84 Number, PositiveInteger,
85};
86pub use self::outline::OutlineStyle;
87pub use self::page::{PageName, PageOrientation, PageSize, PageSizeOrientation, PaperSize};
88pub use self::param::LinkParameters;
89pub use self::percentage::{NoCalcPercentage, NonNegativePercentage, Percentage};
90pub use self::position::{
91 AnchorFunction, AnchorName, AnchorNameIdent, AspectRatio, GridAutoFlow, GridTemplateAreas,
92 Inset, MasonryAutoFlow, MasonryItemOrder, MasonryPlacement, Position, PositionAnchor,
93 PositionAnchorKeyword, PositionArea, PositionAreaKeyword, PositionComponent, PositionOrAuto,
94 PositionTryFallbacks, PositionTryOrder, PositionVisibility, ScopedName, ZIndex,
95};
96pub use self::ratio::Ratio;
97pub use self::rect::NonNegativeLengthOrNumberRect;
98pub use self::resolution::{NoCalcResolution, 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::RubyPosition;
104pub use self::text::{HyphenateCharacter, HyphenateLimitChars};
105pub use self::text::{InitialLetter, LetterSpacing, LineBreak, TextAlign, TextIndent};
106pub use self::text::{OverflowWrap, TextEmphasisPosition, TextEmphasisStyle, WordBreak};
107pub use self::text::{TextAlignKeyword, TextDecorationLine, TextOverflow, WordSpacing};
108pub use self::text::{TextAlignLast, TextAutospace, TextUnderlinePosition};
109pub use self::text::{TextBoxEdge, TextBoxTrim};
110pub use self::text::{
111 TextDecorationInset, TextDecorationLength, TextDecorationSkipInk, TextJustify, TextTransform,
112};
113pub use self::time::{NoCalcTime, Time};
114pub use self::transform::{Rotate, Scale, Transform};
115pub use self::transform::{TransformBox, TransformOrigin, TransformStyle, Translate};
116pub use self::tree_counting::TreeCountingFunction;
117#[cfg(feature = "gecko")]
118pub use self::ui::CursorImage;
119pub use self::ui::{
120 BoolInteger, Cursor, Inert, MozTheme, PointerEvents, ScrollbarColor, UserFocus, UserSelect,
121};
122pub use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
123
124pub mod align;
125pub mod angle;
126pub mod animation;
127pub mod background;
128pub mod basic_shape;
129pub mod border;
130#[path = "box.rs"]
131pub mod box_;
132pub mod calc;
133pub mod color;
134pub mod column;
135pub mod corner_shape;
136pub mod counters;
137pub mod easing;
138pub mod effects;
139pub mod flex;
140pub mod font;
141pub mod frequency;
142pub mod grid;
143pub mod image;
144pub mod intersection_observer;
145pub mod length;
146pub mod list;
147pub mod motion;
148pub mod number;
149pub mod outline;
150pub mod page;
151pub mod param;
152pub mod percentage;
153pub mod position;
154pub mod ratio;
155pub mod rect;
156pub mod resolution;
157pub mod source_size_list;
158pub mod svg;
159pub mod svg_path;
160pub mod table;
161pub mod text;
162pub mod time;
163pub mod transform;
164pub mod tree_counting;
165pub mod ui;
166pub mod url;
167
168#[allow(missing_docs)]
171#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
172pub enum AngleOrPercentage {
173 Percentage(Percentage),
174 Angle(Angle),
175}
176
177impl AngleOrPercentage {
178 fn parse_internal<'i, 't>(
179 context: &ParserContext,
180 input: &mut Parser<'i, 't>,
181 allow_unitless_zero: AllowUnitlessZeroAngle,
182 ) -> Result<Self, ParseError<'i>> {
183 if let Ok(per) = input.try_parse(|i| Percentage::parse(context, i)) {
184 return Ok(AngleOrPercentage::Percentage(per));
185 }
186
187 Angle::parse_internal(context, input, allow_unitless_zero).map(AngleOrPercentage::Angle)
188 }
189
190 pub fn parse_with_unitless<'i, 't>(
193 context: &ParserContext,
194 input: &mut Parser<'i, 't>,
195 ) -> Result<Self, ParseError<'i>> {
196 AngleOrPercentage::parse_internal(context, input, AllowUnitlessZeroAngle::Yes)
197 }
198}
199
200impl Parse for AngleOrPercentage {
201 fn parse<'i, 't>(
202 context: &ParserContext,
203 input: &mut Parser<'i, 't>,
204 ) -> Result<Self, ParseError<'i>> {
205 AngleOrPercentage::parse_internal(context, input, AllowUnitlessZeroAngle::No)
206 }
207}
208
209#[allow(missing_docs)]
216#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
217pub enum NumberOrPercentage {
218 Percentage(Percentage),
219 Number(Number),
220}
221
222impl NumberOrPercentage {
223 fn parse_with_clamping_mode<'i, 't>(
224 context: &ParserContext,
225 input: &mut Parser<'i, 't>,
226 type_: AllowedNumericType,
227 ) -> Result<Self, ParseError<'i>> {
228 if let Ok(per) =
229 input.try_parse(|i| Percentage::parse_with_clamping_mode(context, i, type_))
230 {
231 return Ok(NumberOrPercentage::Percentage(per));
232 }
233
234 parse_number_with_clamping_mode(context, input, type_).map(NumberOrPercentage::Number)
235 }
236
237 pub fn parse_non_negative<'i, 't>(
239 context: &ParserContext,
240 input: &mut Parser<'i, 't>,
241 ) -> Result<Self, ParseError<'i>> {
242 Self::parse_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
243 }
244
245 pub fn to_percentage(self) -> Option<Percentage> {
247 match self {
248 Self::Percentage(p) => Some(p),
249 Self::Number(n) => n.to_percentage(),
250 }
251 }
252
253 pub fn to_number(&self) -> Option<Number> {
255 match self {
256 Self::Percentage(p) => p.to_number(),
257 Self::Number(n) => Some(n.clone()),
258 }
259 }
260
261 pub fn as_percentage(&self) -> Option<&Percentage> {
263 match self {
264 NumberOrPercentage::Percentage(percentage) => Some(percentage),
265 _ => None,
266 }
267 }
268
269 pub fn into_simplified_number(self) -> NumberOrPercentage {
272 match self.as_percentage().and_then(|p| p.get()) {
273 Some(p) => NumberOrPercentage::Number(Number::new(p)),
274 None => self,
275 }
276 }
277
278 pub fn to_computed_value_without_context(&self) -> Result<computed::NumberOrPercentage, ()> {
280 Ok(match self {
281 NumberOrPercentage::Percentage(percentage) => computed::NumberOrPercentage::Percentage(
282 computed::Percentage(percentage.resolve().ok_or(())?),
283 ),
284 NumberOrPercentage::Number(number) => {
285 computed::NumberOrPercentage::Number(number.resolve().ok_or(())?)
286 },
287 })
288 }
289}
290
291impl Parse for NumberOrPercentage {
292 fn parse<'i, 't>(
293 context: &ParserContext,
294 input: &mut Parser<'i, 't>,
295 ) -> Result<Self, ParseError<'i>> {
296 Self::parse_with_clamping_mode(context, input, AllowedNumericType::All)
297 }
298}
299
300pub type NonNegativeNumberOrPercentage = NonNegative<NumberOrPercentage>;
302
303impl NonNegativeNumberOrPercentage {
304 #[inline]
306 pub fn hundred_percent() -> Self {
307 NonNegative(NumberOrPercentage::Percentage(Percentage::hundred()))
308 }
309
310 #[inline]
312 pub fn new_number(n: f32) -> Self {
313 NonNegative(NumberOrPercentage::Number(Number::new(n)))
314 }
315}
316
317impl Parse for NonNegativeNumberOrPercentage {
318 fn parse<'i, 't>(
319 context: &ParserContext,
320 input: &mut Parser<'i, 't>,
321 ) -> Result<Self, ParseError<'i>> {
322 Ok(NonNegative(NumberOrPercentage::parse_non_negative(
323 context, input,
324 )?))
325 }
326}
327
328#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
330pub struct Opacity(NumberOrPercentage);
331
332impl Parse for Opacity {
333 fn parse<'i, 't>(
337 context: &ParserContext,
338 input: &mut Parser<'i, 't>,
339 ) -> Result<Self, ParseError<'i>> {
340 Ok(Opacity(
341 NumberOrPercentage::parse(context, input)?.into_simplified_number(),
342 ))
343 }
344}
345
346impl ToComputedValue for Opacity {
347 type ComputedValue = CSSFloat;
348
349 #[inline]
350 fn to_computed_value(&self, context: &Context) -> CSSFloat {
351 let value = self.0.to_computed_value(context).value();
352 if context.for_animation {
353 value
356 } else {
357 value.min(1.0).max(0.0)
358 }
359 }
360
361 #[inline]
362 fn from_computed_value(computed: &CSSFloat) -> Self {
363 Opacity(NumberOrPercentage::Number(Number::from_computed_value(
364 computed,
365 )))
366 }
367}
368
369pub type TrackBreadth = GenericTrackBreadth<LengthPercentage>;
371
372pub type TrackSize = GenericTrackSize<LengthPercentage>;
374
375pub type ImplicitGridTracks = GenericImplicitGridTracks<TrackSize>;
377
378pub type TrackList = GenericTrackList<LengthPercentage, Integer>;
381
382pub type GridLine = GenericGridLine<Integer>;
384
385pub type GridTemplateComponent = GenericGridTemplateComponent<LengthPercentage, Integer>;
387
388pub type ClipRect = generics::GenericClipRect<LengthOrAuto>;
390
391impl Parse for ClipRect {
392 fn parse<'i, 't>(
393 context: &ParserContext,
394 input: &mut Parser<'i, 't>,
395 ) -> Result<Self, ParseError<'i>> {
396 Self::parse_quirky(context, input, AllowQuirks::No)
397 }
398}
399
400impl ClipRect {
401 fn parse_quirky<'i, 't>(
403 context: &ParserContext,
404 input: &mut Parser<'i, 't>,
405 allow_quirks: AllowQuirks,
406 ) -> Result<Self, ParseError<'i>> {
407 input.expect_function_matching("rect")?;
408
409 fn parse_argument<'i, 't>(
410 context: &ParserContext,
411 input: &mut Parser<'i, 't>,
412 allow_quirks: AllowQuirks,
413 ) -> Result<LengthOrAuto, ParseError<'i>> {
414 LengthOrAuto::parse_quirky(context, input, allow_quirks)
415 }
416
417 input.parse_nested_block(|input| {
418 let top = parse_argument(context, input, allow_quirks)?;
419 let right;
420 let bottom;
421 let left;
422
423 if input.try_parse(|input| input.expect_comma()).is_ok() {
424 right = parse_argument(context, input, allow_quirks)?;
425 input.expect_comma()?;
426 bottom = parse_argument(context, input, allow_quirks)?;
427 input.expect_comma()?;
428 left = parse_argument(context, input, allow_quirks)?;
429 } else {
430 right = parse_argument(context, input, allow_quirks)?;
431 bottom = parse_argument(context, input, allow_quirks)?;
432 left = parse_argument(context, input, allow_quirks)?;
433 }
434
435 Ok(ClipRect {
436 top,
437 right,
438 bottom,
439 left,
440 })
441 })
442 }
443}
444
445pub type ClipRectOrAuto = generics::GenericClipRectOrAuto<ClipRect>;
447
448impl ClipRectOrAuto {
449 pub fn parse_quirky<'i, 't>(
451 context: &ParserContext,
452 input: &mut Parser<'i, 't>,
453 allow_quirks: AllowQuirks,
454 ) -> Result<Self, ParseError<'i>> {
455 if let Ok(v) = input.try_parse(|i| ClipRect::parse_quirky(context, i, allow_quirks)) {
456 return Ok(generics::GenericClipRectOrAuto::Rect(v));
457 }
458 input.expect_ident_matching("auto")?;
459 Ok(generics::GenericClipRectOrAuto::Auto)
460 }
461}
462
463#[derive(Clone, Copy, PartialEq)]
465pub enum AllowQuirks {
466 No,
468 Yes,
470 Always,
472}
473
474impl AllowQuirks {
475 pub fn allowed(self, quirks_mode: QuirksMode) -> bool {
477 match self {
478 AllowQuirks::Always => true,
479 AllowQuirks::No => false,
480 AllowQuirks::Yes => quirks_mode == QuirksMode::Quirks,
481 }
482 }
483}
484
485#[derive(Clone, Debug, PartialEq, MallocSizeOf, ToShmem)]
486pub enum ParsedNamespace {
488 Unknown,
490 Known(Namespace),
492}
493
494impl ParsedNamespace {
495 pub fn parse<'i, 't>(
498 namespaces: &FxHashMap<Prefix, Namespace>,
499 input: &mut Parser<'i, 't>,
500 ) -> Result<Self, ParseError<'i>> {
501 parse_namespace(namespaces, input, true)
506 .map(|(_prefix, namespace)| namespace)
507 }
508}
509
510impl Default for ParsedNamespace {
511 fn default() -> Self {
512 Self::Known(Namespace::default())
513 }
514}
515
516#[derive(
520 Clone,
521 Debug,
522 Eq,
523 MallocSizeOf,
524 PartialEq,
525 SpecifiedValueInfo,
526 ToComputedValue,
527 ToResolvedValue,
528 ToShmem,
529)]
530#[css(function)]
531#[repr(C)]
532pub struct Attr {
533 pub namespace_prefix: Prefix,
535 pub namespace_url: Namespace,
537 pub attribute: Atom,
539 pub fallback: AtomString,
541}
542
543impl Parse for Attr {
544 fn parse<'i, 't>(
545 context: &ParserContext,
546 input: &mut Parser<'i, 't>,
547 ) -> Result<Attr, ParseError<'i>> {
548 input.expect_function_matching("attr")?;
549 input.parse_nested_block(|i| Attr::parse_function(context, i))
550 }
551}
552
553pub fn parse_namespace<'i, 't>(
555 namespaces: &FxHashMap<Prefix, Namespace>,
556 input: &mut Parser<'i, 't>,
557 allow_non_registered: bool,
559) -> Result<(Prefix, ParsedNamespace), ParseError<'i>> {
560 let ns_prefix = match input.next()? {
561 Token::Ident(ref prefix) => Some(Prefix::from(prefix.as_ref())),
562 Token::Delim('|') => None,
563 _ => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
564 };
565
566 if ns_prefix.is_some() && !matches!(*input.next_including_whitespace()?, Token::Delim('|')) {
567 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
568 }
569
570 if let Some(prefix) = ns_prefix {
571 let ns = match namespaces.get(&prefix).cloned() {
572 Some(ns) => ParsedNamespace::Known(ns),
573 None => {
574 if allow_non_registered {
575 ParsedNamespace::Unknown
576 } else {
577 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
578 }
579 },
580 };
581 Ok((prefix, ns))
582 } else {
583 Ok((Prefix::default(), ParsedNamespace::default()))
584 }
585}
586
587impl Attr {
588 pub fn parse_function<'i, 't>(
591 context: &ParserContext,
592 input: &mut Parser<'i, 't>,
593 ) -> Result<Attr, ParseError<'i>> {
594 let namespace = input
596 .try_parse(|input| {
597 parse_namespace(
598 &context.namespaces.prefixes,
599 input,
600 false,
601 )
602 })
603 .ok();
604 let namespace_is_some = namespace.is_some();
605 let (namespace_prefix, namespace_url) = namespace.unwrap_or_default();
606 let ParsedNamespace::Known(namespace_url) = namespace_url else {
607 unreachable!("Non-registered url not allowed (see parse namespace flag).")
608 };
609
610 let attribute = Atom::from(if namespace_is_some {
612 let location = input.current_source_location();
613 match *input.next_including_whitespace()? {
614 Token::Ident(ref ident) => ident.as_ref(),
615 ref t => return Err(location.new_unexpected_token_error(t.clone())),
616 }
617 } else {
618 input.expect_ident()?.as_ref()
619 });
620
621 let fallback = input
624 .try_parse(|input| -> Result<AtomString, ParseError<'i>> {
625 input.expect_comma()?;
626 Ok(input.expect_string()?.as_ref().into())
627 })
628 .unwrap_or_default();
629
630 Ok(Attr {
631 namespace_prefix,
632 namespace_url,
633 attribute,
634 fallback,
635 })
636 }
637}
638
639impl ToCss for Attr {
640 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
641 where
642 W: Write,
643 {
644 dest.write_str("attr(")?;
645 if !self.namespace_prefix.is_empty() {
646 serialize_atom_identifier(&self.namespace_prefix, dest)?;
647 dest.write_char('|')?;
648 }
649 serialize_atom_identifier(&self.attribute, dest)?;
650
651 if !self.fallback.is_empty() {
652 dest.write_str(", ")?;
653 self.fallback.to_css(dest)?;
654 }
655
656 dest.write_char(')')
657 }
658}