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::typed_om::NumericBaseType;
19use crate::values::computed;
20use crate::values::specified::calc::PercentageContext;
21use crate::values::specified::number::parse_number_with_clamping_mode;
22use crate::{Namespace, Prefix};
23use cssparser::{Parser, Token};
24use rustc_hash::FxHashMap;
25use style_traits::values::specified::AllowedNumericType;
26use style_traits::{ParseError, StyleParseErrorKind};
27
28pub use self::align::{ContentDistribution, ItemPlacement, JustifyItems, SelfAlignment};
29pub use self::angle::{AllowUnitlessZeroAngle, Angle, NoCalcAngle};
30pub use self::animation::{
31 AnimationComposition, AnimationDirection, AnimationDuration, AnimationFillMode,
32 AnimationIterationCount, AnimationName, AnimationPlayState, AnimationRangeEnd,
33 AnimationRangeStart, AnimationTimeline, ScrollAxis, TimelineName, TransitionBehavior,
34 TransitionProperty, ViewTimelineInset, ViewTransitionClass, ViewTransitionName,
35};
36pub use self::background::{BackgroundClip, BackgroundRepeat, BackgroundSize};
37pub use self::basic_shape::FillRule;
38pub use self::border::{
39 BorderCornerRadius, BorderImageRepeat, BorderImageSideWidth, BorderImageSlice,
40 BorderImageWidth, BorderRadius, BorderSideOffset, BorderSideWidth, BorderSpacing, BorderStyle,
41 LineWidth,
42};
43pub use self::box_::{
44 AlignmentBaseline, Appearance, BaselineShift, BaselineSource, BreakBetween, BreakWithin, Clear,
45 Contain, ContainIntrinsicSize, ContainerName, ContainerType, ContentVisibility, Display,
46 DominantBaseline, Float, LineClamp, MarginTrim, Overflow, OverflowAnchor, OverflowClipMargin,
47 OverscrollBehavior, Perspective, PositionProperty, Resize, ScrollSnapAlign, ScrollSnapAxis,
48 ScrollSnapStop, ScrollSnapStrictness, ScrollSnapType, ScrollbarGutter, ScrollbarInset,
49 TouchAction, WillChange, WillChangeBits, WritingModeProperty, Zoom,
50};
51pub use self::calc::{CalcLengthPercentage, CalcNumeric};
52pub use self::color::{
53 Color, ColorOrAuto, ColorPropertyValue, ColorScheme, ForcedColorAdjust, PrintColorAdjust,
54};
55pub use self::column::ColumnCount;
56pub use self::corner_shape::{CornerShape, CornerShapeRect, SuperellipseArg};
57pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset, CounterSet};
58pub use self::easing::TimingFunction;
59pub use self::effects::{BoxShadow, Filter, SimpleShadow};
60pub use self::flex::FlexBasis;
61pub use self::font::{FontFamily, FontLanguageOverride, FontPalette, FontStyle};
62pub use self::font::{FontFeatureSettings, FontVariantLigatures, FontVariantNumeric};
63pub use self::font::{
64 FontSize, FontSizeAdjust, FontSizeAdjustFactor, FontSizeKeyword, FontSynthesis,
65 FontSynthesisStyle, FontWidth,
66};
67pub use self::font::{FontVariantAlternates, FontWeight};
68pub use self::font::{FontVariantEastAsian, FontVariationSettings, LineHeight};
69pub use self::font::{MathDepth, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextScale};
70pub use self::image::{
71 EndingShape as GradientEndingShape, Gradient, Image, ImageDecoding, ImageRendering,
72};
73pub use self::length::{Length, LengthOrNumber, LengthUnit, NonNegativeLengthOrNumber};
74pub use self::length::{LengthOrAuto, LengthPercentage, LengthPercentageOrAuto};
75pub use self::length::{Margin, MaxSize, Size};
76pub use self::length::{NoCalcLength, ViewportVariant};
77pub use self::length::{
78 NonNegativeLength, NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto,
79};
80pub use self::list::ListStyleType;
81pub use self::list::Quotes;
82pub use self::motion::{OffsetPath, OffsetPosition, OffsetRotate};
83pub use self::number::{
84 GreaterThanOrEqualToOneNumber, Integer, NoCalcNumber, NonNegativeInteger, NonNegativeNumber,
85 Number, PositiveInteger,
86};
87pub use self::outline::OutlineStyle;
88pub use self::page::{PageName, PageOrientation, PageSize, PageSizeOrientation, PaperSize};
89pub use self::param::LinkParameters;
90pub use self::percentage::{NoCalcPercentage, NonNegativePercentage, Percentage};
91pub use self::position::{
92 AnchorFunction, AnchorName, AnchorNameIdent, AspectRatio, FlexWrap, GridAutoFlow,
93 GridTemplateAreas, Inset, MasonryAutoFlow, MasonryItemOrder, MasonryPlacement, Position,
94 PositionAnchor, PositionAnchorKeyword, PositionArea, PositionAreaKeyword, PositionComponent,
95 PositionOrAuto, PositionTryFallbacks, PositionTryOrder, PositionVisibility, ScopedName, ZIndex,
96};
97pub use self::ratio::Ratio;
98pub use self::rect::NonNegativeLengthOrNumberRect;
99pub use self::resolution::{NoCalcResolution, 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::{TextBoxEdge, TextBoxTrim};
111pub use self::text::{
112 TextDecorationInset, TextDecorationLength, TextDecorationSkipInk, TextJustify, TextTransform,
113};
114pub use self::time::{NoCalcTime, Time};
115pub use self::transform::{Rotate, Scale, Transform};
116pub use self::transform::{TransformBox, TransformOrigin, TransformStyle, Translate};
117pub use self::tree_counting::TreeCountingFunction;
118#[cfg(feature = "gecko")]
119pub use self::ui::CursorImage;
120pub use self::ui::{
121 BoolInteger, Cursor, Inert, MozTheme, PointerEvents, ScrollbarColor, UserFocus, UserSelect,
122};
123pub use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
124
125pub mod align;
126pub mod angle;
127pub mod animation;
128pub mod background;
129pub mod basic_shape;
130pub mod border;
131#[path = "box.rs"]
132pub mod box_;
133pub mod calc;
134pub mod color;
135pub mod column;
136pub mod corner_shape;
137pub mod counters;
138pub mod easing;
139pub mod effects;
140pub mod flex;
141pub mod font;
142pub mod frequency;
143pub mod grid;
144pub mod image;
145pub mod intersection_observer;
146pub mod length;
147pub mod list;
148pub mod motion;
149pub mod number;
150pub mod outline;
151pub mod page;
152pub mod param;
153pub mod percentage;
154pub mod position;
155pub mod ratio;
156pub mod rect;
157pub mod resolution;
158pub mod source_size_list;
159pub mod svg;
160pub mod svg_path;
161pub mod table;
162pub mod text;
163pub mod time;
164pub mod transform;
165pub mod tree_counting;
166pub mod ui;
167pub mod url;
168
169#[allow(missing_docs)]
172#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
173pub enum AngleOrPercentage {
174 Percentage(Percentage),
175 Angle(Angle),
176}
177
178impl AngleOrPercentage {
179 fn parse_internal(
180 context: &ParserContext,
181 input: &mut Parser,
182 allow_unitless_zero: AllowUnitlessZeroAngle,
183 ) -> Result<Self, ParseError> {
184 if let Ok(per) = input.try_parse(|i| Percentage::parse(context, i)) {
185 return Ok(AngleOrPercentage::Percentage(per));
186 }
187
188 Angle::parse_internal(context, input, allow_unitless_zero).map(AngleOrPercentage::Angle)
189 }
190
191 pub fn parse_with_unitless(
194 context: &ParserContext,
195 input: &mut Parser,
196 ) -> Result<Self, ParseError> {
197 AngleOrPercentage::parse_internal(context, input, AllowUnitlessZeroAngle::Yes)
198 }
199}
200
201impl Parse for AngleOrPercentage {
202 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
203 AngleOrPercentage::parse_internal(context, input, AllowUnitlessZeroAngle::No)
204 }
205}
206
207#[allow(missing_docs)]
214#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
215pub enum NumberOrPercentage {
216 Percentage(Percentage),
217 Number(Number),
218}
219
220impl NumberOrPercentage {
221 fn parse_with_clamping_mode(
222 context: &ParserContext,
223 input: &mut Parser,
224 type_: AllowedNumericType,
225 ) -> Result<Self, ParseError> {
226 if let Ok(per) =
227 input.try_parse(|i| Percentage::parse_with_clamping_mode(context, i, type_))
228 {
229 return Ok(NumberOrPercentage::Percentage(per));
230 }
231
232 parse_number_with_clamping_mode(
233 context,
234 input,
235 type_,
236 PercentageContext::allowed_with_hint(NumericBaseType::Percent),
237 )
238 .map(NumberOrPercentage::Number)
239 }
240
241 pub fn parse_non_negative(
243 context: &ParserContext,
244 input: &mut Parser,
245 ) -> Result<Self, ParseError> {
246 Self::parse_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
247 }
248
249 pub fn to_percentage(self) -> Option<Percentage> {
251 match self {
252 Self::Percentage(p) => Some(p),
253 Self::Number(n) => n.to_percentage(),
254 }
255 }
256
257 pub fn to_number(&self) -> Option<Number> {
259 match self {
260 Self::Percentage(p) => p.to_number(),
261 Self::Number(n) => Some(n.clone()),
262 }
263 }
264
265 pub fn as_percentage(&self) -> Option<&Percentage> {
267 match self {
268 NumberOrPercentage::Percentage(percentage) => Some(percentage),
269 _ => None,
270 }
271 }
272
273 pub fn into_simplified_number(self) -> NumberOrPercentage {
276 match self.as_percentage().and_then(|p| p.get()) {
277 Some(p) => NumberOrPercentage::Number(Number::new(p)),
278 None => self,
279 }
280 }
281
282 pub fn to_computed_value_without_context(&self) -> Result<computed::NumberOrPercentage, ()> {
284 Ok(match self {
285 NumberOrPercentage::Percentage(percentage) => computed::NumberOrPercentage::Percentage(
286 computed::Percentage(percentage.resolve().ok_or(())?),
287 ),
288 NumberOrPercentage::Number(number) => {
289 computed::NumberOrPercentage::Number(number.resolve().ok_or(())?)
290 },
291 })
292 }
293}
294
295impl Parse for NumberOrPercentage {
296 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
297 Self::parse_with_clamping_mode(context, input, AllowedNumericType::All)
298 }
299}
300
301pub type NonNegativeNumberOrPercentage = NonNegative<NumberOrPercentage>;
303
304impl NonNegativeNumberOrPercentage {
305 #[inline]
307 pub fn hundred_percent() -> Self {
308 NonNegative(NumberOrPercentage::Percentage(Percentage::hundred()))
309 }
310
311 #[inline]
313 pub fn new_number(n: f32) -> Self {
314 NonNegative(NumberOrPercentage::Number(Number::new(n)))
315 }
316}
317
318impl Parse for NonNegativeNumberOrPercentage {
319 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
320 Ok(NonNegative(NumberOrPercentage::parse_non_negative(
321 context, input,
322 )?))
323 }
324}
325
326#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
328pub struct Opacity(NumberOrPercentage);
329
330impl Parse for Opacity {
331 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
335 Ok(Opacity(
336 NumberOrPercentage::parse(context, input)?.into_simplified_number(),
337 ))
338 }
339}
340
341impl ToComputedValue for Opacity {
342 type ComputedValue = CSSFloat;
343
344 #[inline]
345 fn to_computed_value(&self, context: &Context) -> CSSFloat {
346 let value = self.0.to_computed_value(context).value();
347 if context.for_animation {
348 value
351 } else {
352 value.min(1.0).max(0.0)
353 }
354 }
355
356 #[inline]
357 fn from_computed_value(computed: &CSSFloat) -> Self {
358 Opacity(NumberOrPercentage::Number(Number::from_computed_value(
359 computed,
360 )))
361 }
362}
363
364pub type TrackBreadth = GenericTrackBreadth<LengthPercentage>;
366
367pub type TrackSize = GenericTrackSize<LengthPercentage>;
369
370pub type ImplicitGridTracks = GenericImplicitGridTracks<TrackSize>;
372
373pub type TrackList = GenericTrackList<LengthPercentage, Integer>;
376
377pub type GridLine = GenericGridLine<Integer>;
379
380pub type GridTemplateComponent = GenericGridTemplateComponent<LengthPercentage, Integer>;
382
383pub type ClipRect = generics::GenericClipRect<LengthOrAuto>;
385
386impl Parse for ClipRect {
387 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
388 Self::parse_quirky(context, input, AllowQuirks::No)
389 }
390}
391
392impl ClipRect {
393 fn parse_quirky(
395 context: &ParserContext,
396 input: &mut Parser,
397 allow_quirks: AllowQuirks,
398 ) -> Result<Self, ParseError> {
399 input.expect_function_matching("rect")?;
400
401 fn parse_argument(
402 context: &ParserContext,
403 input: &mut Parser,
404 allow_quirks: AllowQuirks,
405 ) -> Result<LengthOrAuto, ParseError> {
406 LengthOrAuto::parse_quirky(context, input, allow_quirks)
407 }
408
409 input.parse_nested_block(|input| {
410 let top = parse_argument(context, input, allow_quirks)?;
411 let right;
412 let bottom;
413 let left;
414
415 if input.try_parse(|input| input.expect_comma()).is_ok() {
416 right = parse_argument(context, input, allow_quirks)?;
417 input.expect_comma()?;
418 bottom = parse_argument(context, input, allow_quirks)?;
419 input.expect_comma()?;
420 left = parse_argument(context, input, allow_quirks)?;
421 } else {
422 right = parse_argument(context, input, allow_quirks)?;
423 bottom = parse_argument(context, input, allow_quirks)?;
424 left = parse_argument(context, input, allow_quirks)?;
425 }
426
427 Ok(ClipRect {
428 top,
429 right,
430 bottom,
431 left,
432 })
433 })
434 }
435}
436
437pub type ClipRectOrAuto = generics::GenericClipRectOrAuto<ClipRect>;
439
440impl ClipRectOrAuto {
441 pub fn parse_quirky(
443 context: &ParserContext,
444 input: &mut Parser,
445 allow_quirks: AllowQuirks,
446 ) -> Result<Self, ParseError> {
447 if let Ok(v) = input.try_parse(|i| ClipRect::parse_quirky(context, i, allow_quirks)) {
448 return Ok(generics::GenericClipRectOrAuto::Rect(v));
449 }
450 input.expect_ident_matching("auto")?;
451 Ok(generics::GenericClipRectOrAuto::Auto)
452 }
453}
454
455#[derive(Clone, Copy, PartialEq)]
457pub enum AllowQuirks {
458 No,
460 Yes,
462 Always,
464}
465
466impl AllowQuirks {
467 pub fn allowed(self, quirks_mode: QuirksMode) -> bool {
469 match self {
470 AllowQuirks::Always => true,
471 AllowQuirks::No => false,
472 AllowQuirks::Yes => quirks_mode == QuirksMode::Quirks,
473 }
474 }
475}
476
477#[derive(Clone, Debug, PartialEq, MallocSizeOf, ToShmem)]
478pub enum ParsedNamespace {
480 Unknown,
482 Known(Namespace),
484}
485
486impl ParsedNamespace {
487 pub fn parse(
490 namespaces: &FxHashMap<Prefix, Namespace>,
491 input: &mut Parser,
492 ) -> Result<Self, ParseError> {
493 parse_namespace(namespaces, input).map(|(_prefix, namespace)| namespace)
498 }
499}
500
501impl Default for ParsedNamespace {
502 fn default() -> Self {
503 Self::Known(Namespace::default())
504 }
505}
506
507pub fn parse_namespace(
509 namespaces: &FxHashMap<Prefix, Namespace>,
510 input: &mut Parser,
511) -> Result<(Prefix, ParsedNamespace), ParseError> {
512 let ns_prefix = match input.next()? {
513 Token::Ident(prefix) => Some(Prefix::from(prefix.as_ref())),
514 Token::Delim('|') => None,
515 _ => return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError)),
516 };
517
518 if ns_prefix.is_some() && !matches!(*input.next_including_whitespace()?, Token::Delim('|')) {
519 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
520 }
521
522 if let Some(prefix) = ns_prefix {
523 let ns = match namespaces.get(&prefix).cloned() {
524 Some(ns) => ParsedNamespace::Known(ns),
525 None => ParsedNamespace::Unknown,
526 };
527 Ok((prefix, ns))
528 } else {
529 Ok((Prefix::default(), ParsedNamespace::default()))
530 }
531}