1use crate::parser::{Parse, ParserContext};
8use crate::values::generics::box_::PositionProperty;
9use crate::values::generics::Optional;
10use crate::values::DashedIdent;
11use crate::Zero;
12use cssparser::Parser;
13use std::fmt::Write;
14use style_traits::ParseError;
15use style_traits::StyleParseErrorKind;
16use style_traits::ToCss;
17use style_traits::{CssWriter, SpecifiedValueInfo};
18
19#[allow(missing_docs)]
21#[derive(
22 Animate,
23 Clone,
24 ComputeSquaredDistance,
25 Copy,
26 Debug,
27 MallocSizeOf,
28 PartialEq,
29 SpecifiedValueInfo,
30 ToAnimatedValue,
31 ToAnimatedZero,
32 ToComputedValue,
33 ToCss,
34 ToResolvedValue,
35 ToShmem,
36)]
37#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
38#[repr(C, u8)]
39pub enum GenericLengthPercentageOrAuto<LengthPercent> {
40 LengthPercentage(LengthPercent),
41 Auto,
42}
43
44pub use self::GenericLengthPercentageOrAuto as LengthPercentageOrAuto;
45
46impl<LengthPercentage> LengthPercentageOrAuto<LengthPercentage> {
47 #[inline]
49 pub fn auto() -> Self {
50 LengthPercentageOrAuto::Auto
51 }
52
53 #[inline]
55 pub fn is_auto(&self) -> bool {
56 matches!(*self, LengthPercentageOrAuto::Auto)
57 }
58
59 pub fn parse_with<'i, 't>(
61 context: &ParserContext,
62 input: &mut Parser<'i, 't>,
63 parser: impl FnOnce(
64 &ParserContext,
65 &mut Parser<'i, 't>,
66 ) -> Result<LengthPercentage, ParseError<'i>>,
67 ) -> Result<Self, ParseError<'i>> {
68 if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() {
69 return Ok(LengthPercentageOrAuto::Auto);
70 }
71
72 Ok(LengthPercentageOrAuto::LengthPercentage(parser(
73 context, input,
74 )?))
75 }
76}
77
78impl<LengthPercentage> LengthPercentageOrAuto<LengthPercentage>
79where
80 LengthPercentage: Clone,
81{
82 #[inline]
84 pub fn auto_is(&self, f: impl FnOnce() -> LengthPercentage) -> LengthPercentage {
85 match self {
86 LengthPercentageOrAuto::LengthPercentage(length) => length.clone(),
87 LengthPercentageOrAuto::Auto => f(),
88 }
89 }
90
91 #[inline]
93 pub fn non_auto(&self) -> Option<LengthPercentage> {
94 match self {
95 LengthPercentageOrAuto::LengthPercentage(length) => Some(length.clone()),
96 LengthPercentageOrAuto::Auto => None,
97 }
98 }
99
100 pub fn map<T>(&self, f: impl FnOnce(LengthPercentage) -> T) -> LengthPercentageOrAuto<T> {
102 match self {
103 LengthPercentageOrAuto::LengthPercentage(l) => {
104 LengthPercentageOrAuto::LengthPercentage(f(l.clone()))
105 },
106 LengthPercentageOrAuto::Auto => LengthPercentageOrAuto::Auto,
107 }
108 }
109}
110
111impl<LengthPercentage: Zero> Zero for LengthPercentageOrAuto<LengthPercentage> {
112 fn zero() -> Self {
113 LengthPercentageOrAuto::LengthPercentage(Zero::zero())
114 }
115
116 fn is_zero(&self) -> bool {
117 match *self {
118 LengthPercentageOrAuto::LengthPercentage(ref l) => l.is_zero(),
119 LengthPercentageOrAuto::Auto => false,
120 }
121 }
122}
123
124impl<LengthPercentage: Parse> Parse for LengthPercentageOrAuto<LengthPercentage> {
125 fn parse<'i, 't>(
126 context: &ParserContext,
127 input: &mut Parser<'i, 't>,
128 ) -> Result<Self, ParseError<'i>> {
129 Self::parse_with(context, input, LengthPercentage::parse)
130 }
131}
132
133#[allow(missing_docs)]
140#[derive(
141 Animate,
142 ComputeSquaredDistance,
143 Clone,
144 Debug,
145 MallocSizeOf,
146 PartialEq,
147 ToAnimatedValue,
148 ToAnimatedZero,
149 ToComputedValue,
150 ToCss,
151 ToResolvedValue,
152 ToShmem,
153)]
154#[repr(C, u8)]
155pub enum GenericSize<LengthPercent> {
156 LengthPercentage(LengthPercent),
157 Auto,
158 #[animation(error)]
159 MaxContent,
160 #[animation(error)]
161 MinContent,
162 #[animation(error)]
163 FitContent,
164 #[cfg(feature = "gecko")]
165 #[animation(error)]
166 MozAvailable,
167 #[cfg(feature = "gecko")]
168 #[animation(error)]
169 WebkitFillAvailable,
170 #[animation(error)]
171 Stretch,
172 #[animation(error)]
173 #[css(function = "fit-content")]
174 FitContentFunction(LengthPercent),
175 AnchorSizeFunction(
176 #[animation(field_bound)]
177 #[distance(field_bound)]
178 Box<GenericAnchorSizeFunction<LengthPercent>>
179 ),
180 AnchorContainingCalcFunction(LengthPercent),
181}
182
183impl<LengthPercent> SpecifiedValueInfo for GenericSize<LengthPercent>
184where
185LengthPercent: SpecifiedValueInfo
186{
187 fn collect_completion_keywords(f: style_traits::KeywordsCollectFn) {
188 LengthPercent::collect_completion_keywords(f);
189 f(&["auto", "stretch", "fit-content"]);
190 if cfg!(feature = "gecko") {
191 f(&["max-content", "min-content", "-moz-available", "-webkit-fill-available"]);
192 }
193 if static_prefs::pref!("layout.css.anchor-positioning.enabled") {
194 f(&["anchor-size"]);
195 }
196 }
197}
198
199pub use self::GenericSize as Size;
200
201impl<LengthPercentage> Size<LengthPercentage> {
202 #[inline]
204 pub fn auto() -> Self {
205 Size::Auto
206 }
207
208 #[inline]
210 pub fn is_auto(&self) -> bool {
211 matches!(*self, Size::Auto)
212 }
213}
214
215#[allow(missing_docs)]
217#[derive(
218 Animate,
219 Clone,
220 ComputeSquaredDistance,
221 Debug,
222 MallocSizeOf,
223 PartialEq,
224 ToAnimatedValue,
225 ToAnimatedZero,
226 ToComputedValue,
227 ToCss,
228 ToResolvedValue,
229 ToShmem,
230)]
231#[repr(C, u8)]
232pub enum GenericMaxSize<LengthPercent> {
233 LengthPercentage(LengthPercent),
234 None,
235 #[animation(error)]
236 MaxContent,
237 #[animation(error)]
238 MinContent,
239 #[animation(error)]
240 FitContent,
241 #[cfg(feature = "gecko")]
242 #[animation(error)]
243 MozAvailable,
244 #[cfg(feature = "gecko")]
245 #[animation(error)]
246 WebkitFillAvailable,
247 #[animation(error)]
248 Stretch,
249 #[animation(error)]
250 #[css(function = "fit-content")]
251 FitContentFunction(LengthPercent),
252 AnchorSizeFunction(
253 #[animation(field_bound)]
254 #[distance(field_bound)]
255 Box<GenericAnchorSizeFunction<LengthPercent>>
256 ),
257 AnchorContainingCalcFunction(LengthPercent),
258}
259
260impl<LP> SpecifiedValueInfo for GenericMaxSize<LP>
261where
262 LP: SpecifiedValueInfo
263{
264 fn collect_completion_keywords(f: style_traits::KeywordsCollectFn) {
265 LP::collect_completion_keywords(f);
266 f(&["none", "stretch", "fit-content"]);
267 if cfg!(feature = "gecko") {
268 f(&["max-content", "min-content", "-moz-available", "-webkit-fill-available"]);
269 }
270 if static_prefs::pref!("layout.css.anchor-positioning.enabled") {
271 f(&["anchor-size"]);
272 }
273 }
274}
275
276pub use self::GenericMaxSize as MaxSize;
277
278impl<LengthPercentage> MaxSize<LengthPercentage> {
279 #[inline]
281 pub fn none() -> Self {
282 MaxSize::None
283 }
284}
285
286#[derive(
288 Animate,
289 Clone,
290 ComputeSquaredDistance,
291 Copy,
292 Debug,
293 MallocSizeOf,
294 Parse,
295 PartialEq,
296 SpecifiedValueInfo,
297 ToAnimatedValue,
298 ToAnimatedZero,
299 ToComputedValue,
300 ToCss,
301 ToResolvedValue,
302 ToShmem,
303)]
304#[repr(C, u8)]
305pub enum GenericLengthOrNumber<L, N> {
306 Number(N),
311 Length(L),
313}
314
315pub use self::GenericLengthOrNumber as LengthOrNumber;
316
317impl<L, N: Zero> Zero for LengthOrNumber<L, N> {
318 fn zero() -> Self {
319 LengthOrNumber::Number(Zero::zero())
320 }
321
322 fn is_zero(&self) -> bool {
323 match *self {
324 LengthOrNumber::Number(ref n) => n.is_zero(),
325 LengthOrNumber::Length(..) => false,
326 }
327 }
328}
329
330#[derive(
332 Animate,
333 Clone,
334 ComputeSquaredDistance,
335 Copy,
336 Debug,
337 MallocSizeOf,
338 Parse,
339 PartialEq,
340 SpecifiedValueInfo,
341 ToAnimatedValue,
342 ToAnimatedZero,
343 ToComputedValue,
344 ToCss,
345 ToResolvedValue,
346 ToShmem,
347)]
348#[repr(C, u8)]
349#[allow(missing_docs)]
350pub enum GenericLengthPercentageOrNormal<LengthPercent> {
351 LengthPercentage(LengthPercent),
352 Normal,
353}
354
355pub use self::GenericLengthPercentageOrNormal as LengthPercentageOrNormal;
356
357impl<LengthPercent> LengthPercentageOrNormal<LengthPercent> {
358 #[inline]
360 pub fn normal() -> Self {
361 LengthPercentageOrNormal::Normal
362 }
363}
364
365#[derive(
370 Animate,
371 Clone,
372 ComputeSquaredDistance,
373 Debug,
374 MallocSizeOf,
375 PartialEq,
376 SpecifiedValueInfo,
377 ToShmem,
378 ToAnimatedValue,
379 ToAnimatedZero,
380 ToComputedValue,
381 ToResolvedValue,
382 Serialize,
383 Deserialize,
384)]
385#[repr(C)]
386pub struct GenericAnchorSizeFunction<LengthPercentage> {
387 #[animation(constant)]
390 pub target_element: DashedIdent,
391 pub size: AnchorSizeKeyword,
394 pub fallback: Optional<LengthPercentage>,
396}
397
398impl<LengthPercentage> ToCss for GenericAnchorSizeFunction<LengthPercentage>
399where
400 LengthPercentage: ToCss,
401{
402 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> std::fmt::Result
403 where
404 W: Write,
405 {
406 dest.write_str("anchor-size(")?;
407 let mut previous_entry_printed = false;
408 if !self.target_element.is_empty() {
409 previous_entry_printed = true;
410 self.target_element.to_css(dest)?;
411 }
412 if self.size != AnchorSizeKeyword::None {
413 if previous_entry_printed {
414 dest.write_str(" ")?;
415 }
416 previous_entry_printed = true;
417 self.size.to_css(dest)?;
418 }
419 if let Some(f) = self.fallback.as_ref() {
420 if previous_entry_printed {
421 dest.write_str(", ")?;
422 }
423 f.to_css(dest)?;
424 }
425 dest.write_str(")")
426 }
427}
428
429impl<LengthPercentage> Parse for GenericAnchorSizeFunction<LengthPercentage>
430where
431 LengthPercentage: Parse,
432{
433 fn parse<'i, 't>(
434 context: &ParserContext,
435 input: &mut Parser<'i, 't>,
436 ) -> Result<Self, ParseError<'i>> {
437 if !static_prefs::pref!("layout.css.anchor-positioning.enabled") {
438 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
439 }
440 input.expect_function_matching("anchor-size")?;
441 Self::parse_inner(
442 context,
443 input,
444 |i| LengthPercentage::parse(context, i)
445 )
446 }
447}
448
449pub enum AnchorResolutionResult<'a, LengthPercentage> {
451 Resolved(LengthPercentage),
453 Fallback(&'a LengthPercentage),
455 Invalid,
457}
458
459impl<'a, LengthPercentage> AnchorResolutionResult<'a, LengthPercentage> {
460 pub fn new_anchor_invalid(fallback: Option<&'a LengthPercentage>) -> Self {
462 if let Some(fb) = fallback {
463 return Self::Fallback(fb);
464 }
465 Self::Invalid
466 }
467}
468
469impl<LengthPercentage> GenericAnchorSizeFunction<LengthPercentage>
470{
471 pub fn parse_inner<'i, 't, F>(
473 context: &ParserContext,
474 input: &mut Parser<'i, 't>,
475 f: F,
476 ) -> Result<Self, ParseError<'i>>
477 where
478 F: FnOnce(&mut Parser<'i, '_>) -> Result<LengthPercentage, ParseError<'i>>,
479 {
480 input.parse_nested_block(|i| {
481 let mut target_element = i
482 .try_parse(|i| DashedIdent::parse(context, i))
483 .unwrap_or(DashedIdent::empty());
484 let size = i.try_parse(AnchorSizeKeyword::parse).unwrap_or(AnchorSizeKeyword::None);
485 if target_element.is_empty() {
486 target_element = i
487 .try_parse(|i| DashedIdent::parse(context, i))
488 .unwrap_or(DashedIdent::empty());
489 }
490 let previous_parsed = !target_element.is_empty() || size != AnchorSizeKeyword::None;
491 let fallback = i
492 .try_parse(|i| {
493 if previous_parsed {
494 i.expect_comma()?;
495 }
496 f(i)
497 })
498 .ok();
499 Ok(GenericAnchorSizeFunction {
500 target_element,
501 size: size.into(),
502 fallback: fallback.into(),
503 })
504 })
505 }
506
507 pub fn resolve<'a>(
509 &'a self,
510 position_property: PositionProperty,
511 ) -> AnchorResolutionResult<'a, LengthPercentage> {
512 if !position_property.is_absolutely_positioned() {
513 return AnchorResolutionResult::new_anchor_invalid(self.fallback.as_ref());
514 }
515
516 AnchorResolutionResult::new_anchor_invalid(self.fallback.as_ref())
518 }
519}
520
521#[derive(
523 Animate,
524 Clone,
525 ComputeSquaredDistance,
526 Copy,
527 Debug,
528 MallocSizeOf,
529 PartialEq,
530 Parse,
531 SpecifiedValueInfo,
532 ToCss,
533 ToShmem,
534 ToAnimatedValue,
535 ToAnimatedZero,
536 ToComputedValue,
537 ToResolvedValue,
538 Serialize,
539 Deserialize,
540)]
541#[repr(u8)]
542pub enum AnchorSizeKeyword {
543 #[css(skip)]
545 None,
546 Width,
548 Height,
550 Block,
552 Inline,
554 SelfBlock,
556 SelfInline,
558}
559
560#[derive(
563 Animate,
564 Clone,
565 ComputeSquaredDistance,
566 Debug,
567 MallocSizeOf,
568 PartialEq,
569 ToCss,
570 ToShmem,
571 ToAnimatedValue,
572 ToAnimatedZero,
573 ToComputedValue,
574 ToResolvedValue,
575)]
576#[repr(C)]
577pub enum GenericMargin<LP> {
578 LengthPercentage(LP),
580 Auto,
582 AnchorSizeFunction(
586 #[animation(field_bound)]
587 #[distance(field_bound)]
588 Box<GenericAnchorSizeFunction<LP>>,
589 ),
590 AnchorContainingCalcFunction(LP),
593}
594
595#[cfg(feature = "servo")]
596impl<LP> GenericMargin<LP> {
597 #[inline]
599 pub fn is_auto(&self) -> bool {
600 matches!(self, Self::Auto)
601 }
602}
603
604#[cfg(feature = "servo")]
605impl GenericMargin<crate::values::computed::LengthPercentage> {
606 #[inline]
608 pub fn is_definitely_zero(&self) -> bool {
609 match self {
610 Self::LengthPercentage(lp) => lp.is_definitely_zero(),
611 _ => false,
612 }
613 }
614}
615
616impl<LP> SpecifiedValueInfo for GenericMargin<LP>
617where
618 LP: SpecifiedValueInfo,
619{
620 fn collect_completion_keywords(f: style_traits::KeywordsCollectFn) {
621 LP::collect_completion_keywords(f);
622 f(&["auto"]);
623 if static_prefs::pref!("layout.css.anchor-positioning.enabled") {
624 f(&["anchor-size"]);
625 }
626 }
627}
628
629impl<LP> Zero for GenericMargin<LP>
630where
631 LP: Zero,
632{
633 fn is_zero(&self) -> bool {
634 match self {
635 Self::LengthPercentage(l) => l.is_zero(),
636 Self::Auto | Self::AnchorSizeFunction(_) | Self::AnchorContainingCalcFunction(_) => {
637 false
638 },
639 }
640 }
641
642 fn zero() -> Self {
643 Self::LengthPercentage(LP::zero())
644 }
645}