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::{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::{EndingShape as GradientEndingShape, Gradient, Image, ImageRendering};
70pub use self::length::{Length, LengthOrNumber, LengthUnit, NonNegativeLengthOrNumber};
71pub use self::length::{LengthOrAuto, LengthPercentage, LengthPercentageOrAuto};
72pub use self::length::{Margin, MaxSize, Size};
73pub use self::length::{NoCalcLength, ViewportVariant};
74pub use self::length::{
75 NonNegativeLength, NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto,
76};
77pub use self::list::ListStyleType;
78pub use self::list::Quotes;
79pub use self::motion::{OffsetPath, OffsetPosition, OffsetRotate};
80pub use self::number::{
81 GreaterThanOrEqualToOneNumber, Integer, NoCalcNumber, NonNegativeInteger, NonNegativeNumber,
82 Number, PositiveInteger,
83};
84pub use self::outline::OutlineStyle;
85pub use self::page::{PageName, PageOrientation, PageSize, PageSizeOrientation, PaperSize};
86pub use self::percentage::{NoCalcPercentage, NonNegativePercentage, Percentage};
87pub use self::position::{
88 AnchorFunction, AnchorName, AnchorNameIdent, AspectRatio, GridAutoFlow, GridTemplateAreas,
89 Inset, MasonryAutoFlow, MasonryItemOrder, MasonryPlacement, Position, PositionAnchor,
90 PositionAnchorKeyword, PositionArea, PositionAreaKeyword, PositionComponent, PositionOrAuto,
91 PositionTryFallbacks, PositionTryOrder, PositionVisibility, ScopedName, ZIndex,
92};
93pub use self::ratio::Ratio;
94pub use self::rect::NonNegativeLengthOrNumberRect;
95pub use self::resolution::{NoCalcResolution, Resolution};
96pub use self::svg::{DProperty, MozContextProperties};
97pub use self::svg::{SVGLength, SVGOpacity, SVGPaint};
98pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth, VectorEffect};
99pub use self::svg_path::SVGPathData;
100pub use self::text::RubyPosition;
101pub use self::text::{HyphenateCharacter, HyphenateLimitChars};
102pub use self::text::{InitialLetter, LetterSpacing, LineBreak, TextAlign, TextIndent};
103pub use self::text::{OverflowWrap, TextEmphasisPosition, TextEmphasisStyle, WordBreak};
104pub use self::text::{TextAlignKeyword, TextDecorationLine, TextOverflow, WordSpacing};
105pub use self::text::{TextAlignLast, TextAutospace, TextUnderlinePosition};
106pub use self::text::{TextBoxEdge, TextBoxTrim};
107pub use self::text::{
108 TextDecorationInset, TextDecorationLength, TextDecorationSkipInk, TextJustify, TextTransform,
109};
110pub use self::time::{NoCalcTime, Time};
111pub use self::transform::{Rotate, Scale, Transform};
112pub use self::transform::{TransformBox, TransformOrigin, TransformStyle, Translate};
113#[cfg(feature = "gecko")]
114pub use self::ui::CursorImage;
115pub use self::ui::{
116 BoolInteger, Cursor, Inert, MozTheme, PointerEvents, ScrollbarColor, UserFocus, UserSelect,
117};
118pub use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
119
120pub mod align;
121pub mod angle;
122pub mod animation;
123pub mod background;
124pub mod basic_shape;
125pub mod border;
126#[path = "box.rs"]
127pub mod box_;
128pub mod calc;
129pub mod color;
130pub mod column;
131pub mod corner_shape;
132pub mod counters;
133pub mod easing;
134pub mod effects;
135pub mod flex;
136pub mod font;
137pub mod grid;
138pub mod image;
139pub mod intersection_observer;
140pub mod length;
141pub mod list;
142pub mod motion;
143pub mod number;
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, 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
202#[allow(missing_docs)]
209#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
210pub enum NumberOrPercentage {
211 Percentage(Percentage),
212 Number(Number),
213}
214
215impl NumberOrPercentage {
216 fn parse_with_clamping_mode<'i, 't>(
217 context: &ParserContext,
218 input: &mut Parser<'i, 't>,
219 type_: AllowedNumericType,
220 ) -> Result<Self, ParseError<'i>> {
221 if let Ok(per) =
222 input.try_parse(|i| Percentage::parse_with_clamping_mode(context, i, type_))
223 {
224 return Ok(NumberOrPercentage::Percentage(per));
225 }
226
227 parse_number_with_clamping_mode(context, input, type_).map(NumberOrPercentage::Number)
228 }
229
230 pub fn parse_non_negative<'i, 't>(
232 context: &ParserContext,
233 input: &mut Parser<'i, 't>,
234 ) -> Result<Self, ParseError<'i>> {
235 Self::parse_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
236 }
237
238 pub fn to_percentage(self) -> Option<Percentage> {
240 match self {
241 Self::Percentage(p) => Some(p),
242 Self::Number(n) => n.to_percentage(),
243 }
244 }
245
246 pub fn to_number(&self) -> Option<Number> {
248 match self {
249 Self::Percentage(p) => p.to_number(),
250 Self::Number(n) => Some(n.clone()),
251 }
252 }
253
254 pub fn as_percentage(&self) -> Option<&Percentage> {
256 match self {
257 NumberOrPercentage::Percentage(percentage) => Some(percentage),
258 _ => None,
259 }
260 }
261
262 pub fn into_simplified_number(self) -> NumberOrPercentage {
265 match self.as_percentage().and_then(|p| p.get()) {
266 Some(p) => NumberOrPercentage::Number(Number::new(p)),
267 None => self,
268 }
269 }
270
271 pub fn to_computed_value_without_context(&self) -> Result<computed::NumberOrPercentage, ()> {
273 Ok(match self {
274 NumberOrPercentage::Percentage(percentage) => computed::NumberOrPercentage::Percentage(
275 computed::Percentage(percentage.resolve().ok_or(())?),
276 ),
277 NumberOrPercentage::Number(number) => {
278 computed::NumberOrPercentage::Number(number.resolve().ok_or(())?)
279 },
280 })
281 }
282}
283
284impl Parse for NumberOrPercentage {
285 fn parse<'i, 't>(
286 context: &ParserContext,
287 input: &mut Parser<'i, 't>,
288 ) -> Result<Self, ParseError<'i>> {
289 Self::parse_with_clamping_mode(context, input, AllowedNumericType::All)
290 }
291}
292
293pub type NonNegativeNumberOrPercentage = NonNegative<NumberOrPercentage>;
295
296impl NonNegativeNumberOrPercentage {
297 #[inline]
299 pub fn hundred_percent() -> Self {
300 NonNegative(NumberOrPercentage::Percentage(Percentage::hundred()))
301 }
302
303 #[inline]
305 pub fn new_number(n: f32) -> Self {
306 NonNegative(NumberOrPercentage::Number(Number::new(n)))
307 }
308}
309
310impl Parse for NonNegativeNumberOrPercentage {
311 fn parse<'i, 't>(
312 context: &ParserContext,
313 input: &mut Parser<'i, 't>,
314 ) -> Result<Self, ParseError<'i>> {
315 Ok(NonNegative(NumberOrPercentage::parse_non_negative(
316 context, input,
317 )?))
318 }
319}
320
321#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
323pub struct Opacity(NumberOrPercentage);
324
325impl Parse for Opacity {
326 fn parse<'i, 't>(
330 context: &ParserContext,
331 input: &mut Parser<'i, 't>,
332 ) -> Result<Self, ParseError<'i>> {
333 Ok(Opacity(
334 NumberOrPercentage::parse(context, input)?.into_simplified_number(),
335 ))
336 }
337}
338
339impl ToComputedValue for Opacity {
340 type ComputedValue = CSSFloat;
341
342 #[inline]
343 fn to_computed_value(&self, context: &Context) -> CSSFloat {
344 let value = self.0.to_computed_value(context).value();
345 if context.for_animation {
346 value
349 } else {
350 value.min(1.0).max(0.0)
351 }
352 }
353
354 #[inline]
355 fn from_computed_value(computed: &CSSFloat) -> Self {
356 Opacity(NumberOrPercentage::Number(Number::from_computed_value(
357 computed,
358 )))
359 }
360}
361
362pub type TrackBreadth = GenericTrackBreadth<LengthPercentage>;
364
365pub type TrackSize = GenericTrackSize<LengthPercentage>;
367
368pub type ImplicitGridTracks = GenericImplicitGridTracks<TrackSize>;
370
371pub type TrackList = GenericTrackList<LengthPercentage, Integer>;
374
375pub type GridLine = GenericGridLine<Integer>;
377
378pub type GridTemplateComponent = GenericGridTemplateComponent<LengthPercentage, Integer>;
380
381pub type ClipRect = generics::GenericClipRect<LengthOrAuto>;
383
384impl Parse for ClipRect {
385 fn parse<'i, 't>(
386 context: &ParserContext,
387 input: &mut Parser<'i, 't>,
388 ) -> Result<Self, ParseError<'i>> {
389 Self::parse_quirky(context, input, AllowQuirks::No)
390 }
391}
392
393impl ClipRect {
394 fn parse_quirky<'i, 't>(
396 context: &ParserContext,
397 input: &mut Parser<'i, 't>,
398 allow_quirks: AllowQuirks,
399 ) -> Result<Self, ParseError<'i>> {
400 input.expect_function_matching("rect")?;
401
402 fn parse_argument<'i, 't>(
403 context: &ParserContext,
404 input: &mut Parser<'i, 't>,
405 allow_quirks: AllowQuirks,
406 ) -> Result<LengthOrAuto, ParseError<'i>> {
407 LengthOrAuto::parse_quirky(context, input, allow_quirks)
408 }
409
410 input.parse_nested_block(|input| {
411 let top = parse_argument(context, input, allow_quirks)?;
412 let right;
413 let bottom;
414 let left;
415
416 if input.try_parse(|input| input.expect_comma()).is_ok() {
417 right = parse_argument(context, input, allow_quirks)?;
418 input.expect_comma()?;
419 bottom = parse_argument(context, input, allow_quirks)?;
420 input.expect_comma()?;
421 left = parse_argument(context, input, allow_quirks)?;
422 } else {
423 right = parse_argument(context, input, allow_quirks)?;
424 bottom = parse_argument(context, input, allow_quirks)?;
425 left = parse_argument(context, input, allow_quirks)?;
426 }
427
428 Ok(ClipRect {
429 top,
430 right,
431 bottom,
432 left,
433 })
434 })
435 }
436}
437
438pub type ClipRectOrAuto = generics::GenericClipRectOrAuto<ClipRect>;
440
441impl ClipRectOrAuto {
442 pub fn parse_quirky<'i, 't>(
444 context: &ParserContext,
445 input: &mut Parser<'i, 't>,
446 allow_quirks: AllowQuirks,
447 ) -> Result<Self, ParseError<'i>> {
448 if let Ok(v) = input.try_parse(|i| ClipRect::parse_quirky(context, i, allow_quirks)) {
449 return Ok(generics::GenericClipRectOrAuto::Rect(v));
450 }
451 input.expect_ident_matching("auto")?;
452 Ok(generics::GenericClipRectOrAuto::Auto)
453 }
454}
455
456#[derive(Clone, Copy, PartialEq)]
458pub enum AllowQuirks {
459 No,
461 Yes,
463 Always,
465}
466
467impl AllowQuirks {
468 pub fn allowed(self, quirks_mode: QuirksMode) -> bool {
470 match self {
471 AllowQuirks::Always => true,
472 AllowQuirks::No => false,
473 AllowQuirks::Yes => quirks_mode == QuirksMode::Quirks,
474 }
475 }
476}
477
478#[derive(Clone, Debug, PartialEq, MallocSizeOf, ToShmem)]
479pub enum ParsedNamespace {
481 Unknown,
483 Known(Namespace),
485}
486
487impl ParsedNamespace {
488 pub fn parse<'i, 't>(
491 namespaces: &FxHashMap<Prefix, Namespace>,
492 input: &mut Parser<'i, 't>,
493 ) -> Result<Self, ParseError<'i>> {
494 parse_namespace(namespaces, input, true)
499 .map(|(_prefix, namespace)| namespace)
500 }
501}
502
503impl Default for ParsedNamespace {
504 fn default() -> Self {
505 Self::Known(Namespace::default())
506 }
507}
508
509#[derive(
513 Clone,
514 Debug,
515 Eq,
516 MallocSizeOf,
517 PartialEq,
518 SpecifiedValueInfo,
519 ToComputedValue,
520 ToResolvedValue,
521 ToShmem,
522)]
523#[css(function)]
524#[repr(C)]
525pub struct Attr {
526 pub namespace_prefix: Prefix,
528 pub namespace_url: Namespace,
530 pub attribute: Atom,
532 pub fallback: AtomString,
534}
535
536impl Parse for Attr {
537 fn parse<'i, 't>(
538 context: &ParserContext,
539 input: &mut Parser<'i, 't>,
540 ) -> Result<Attr, ParseError<'i>> {
541 input.expect_function_matching("attr")?;
542 input.parse_nested_block(|i| Attr::parse_function(context, i))
543 }
544}
545
546pub fn parse_namespace<'i, 't>(
548 namespaces: &FxHashMap<Prefix, Namespace>,
549 input: &mut Parser<'i, 't>,
550 allow_non_registered: bool,
552) -> Result<(Prefix, ParsedNamespace), ParseError<'i>> {
553 let ns_prefix = match input.next()? {
554 Token::Ident(ref prefix) => Some(Prefix::from(prefix.as_ref())),
555 Token::Delim('|') => None,
556 _ => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
557 };
558
559 if ns_prefix.is_some() && !matches!(*input.next_including_whitespace()?, Token::Delim('|')) {
560 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
561 }
562
563 if let Some(prefix) = ns_prefix {
564 let ns = match namespaces.get(&prefix).cloned() {
565 Some(ns) => ParsedNamespace::Known(ns),
566 None => {
567 if allow_non_registered {
568 ParsedNamespace::Unknown
569 } else {
570 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
571 }
572 },
573 };
574 Ok((prefix, ns))
575 } else {
576 Ok((Prefix::default(), ParsedNamespace::default()))
577 }
578}
579
580impl Attr {
581 pub fn parse_function<'i, 't>(
584 context: &ParserContext,
585 input: &mut Parser<'i, 't>,
586 ) -> Result<Attr, ParseError<'i>> {
587 let namespace = input
589 .try_parse(|input| {
590 parse_namespace(
591 &context.namespaces.prefixes,
592 input,
593 false,
594 )
595 })
596 .ok();
597 let namespace_is_some = namespace.is_some();
598 let (namespace_prefix, namespace_url) = namespace.unwrap_or_default();
599 let ParsedNamespace::Known(namespace_url) = namespace_url else {
600 unreachable!("Non-registered url not allowed (see parse namespace flag).")
601 };
602
603 let attribute = Atom::from(if namespace_is_some {
605 let location = input.current_source_location();
606 match *input.next_including_whitespace()? {
607 Token::Ident(ref ident) => ident.as_ref(),
608 ref t => return Err(location.new_unexpected_token_error(t.clone())),
609 }
610 } else {
611 input.expect_ident()?.as_ref()
612 });
613
614 let fallback = input
617 .try_parse(|input| -> Result<AtomString, ParseError<'i>> {
618 input.expect_comma()?;
619 Ok(input.expect_string()?.as_ref().into())
620 })
621 .unwrap_or_default();
622
623 Ok(Attr {
624 namespace_prefix,
625 namespace_url,
626 attribute,
627 fallback,
628 })
629 }
630}
631
632impl ToCss for Attr {
633 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
634 where
635 W: Write,
636 {
637 dest.write_str("attr(")?;
638 if !self.namespace_prefix.is_empty() {
639 serialize_atom_identifier(&self.namespace_prefix, dest)?;
640 dest.write_char('|')?;
641 }
642 serialize_atom_identifier(&self.attribute, dest)?;
643
644 if !self.fallback.is_empty() {
645 dest.write_str(", ")?;
646 self.fallback.to_css(dest)?;
647 }
648
649 dest.write_char(')')
650 }
651}