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}
448impl<LengthPercentage> GenericAnchorSizeFunction<LengthPercentage> {
449 pub fn valid_for(
451 &self,
452 position_property: PositionProperty,
453 ) -> bool {
454 position_property.is_absolutely_positioned()
455 }
456}
457
458pub enum AnchorResolutionResult<'a, LengthPercentage> {
460 Resolved(LengthPercentage),
462 Fallback(&'a LengthPercentage),
464 Invalid,
466}
467
468impl<'a, LengthPercentage> AnchorResolutionResult<'a, LengthPercentage> {
469 pub fn new_anchor_invalid(fallback: Option<&'a LengthPercentage>) -> Self {
471 if let Some(fb) = fallback {
472 return Self::Fallback(fb);
473 }
474 Self::Invalid
475 }
476}
477
478impl<LengthPercentage> GenericAnchorSizeFunction<LengthPercentage>
479{
480 pub fn parse_inner<'i, 't, F>(
482 context: &ParserContext,
483 input: &mut Parser<'i, 't>,
484 f: F,
485 ) -> Result<Self, ParseError<'i>>
486 where
487 F: FnOnce(&mut Parser<'i, '_>) -> Result<LengthPercentage, ParseError<'i>>,
488 {
489 input.parse_nested_block(|i| {
490 let mut target_element = i
491 .try_parse(|i| DashedIdent::parse(context, i))
492 .unwrap_or(DashedIdent::empty());
493 let size = i.try_parse(AnchorSizeKeyword::parse).unwrap_or(AnchorSizeKeyword::None);
494 if target_element.is_empty() {
495 target_element = i
496 .try_parse(|i| DashedIdent::parse(context, i))
497 .unwrap_or(DashedIdent::empty());
498 }
499 let previous_parsed = !target_element.is_empty() || size != AnchorSizeKeyword::None;
500 let fallback = i
501 .try_parse(|i| {
502 if previous_parsed {
503 i.expect_comma()?;
504 }
505 f(i)
506 })
507 .ok();
508 Ok(GenericAnchorSizeFunction {
509 target_element,
510 size: size.into(),
511 fallback: fallback.into(),
512 })
513 })
514 }
515}
516
517#[derive(
519 Animate,
520 Clone,
521 ComputeSquaredDistance,
522 Copy,
523 Debug,
524 MallocSizeOf,
525 PartialEq,
526 Parse,
527 SpecifiedValueInfo,
528 ToCss,
529 ToShmem,
530 ToAnimatedValue,
531 ToAnimatedZero,
532 ToComputedValue,
533 ToResolvedValue,
534 Serialize,
535 Deserialize,
536)]
537#[repr(u8)]
538pub enum AnchorSizeKeyword {
539 #[css(skip)]
541 None,
542 Width,
544 Height,
546 Block,
548 Inline,
550 SelfBlock,
552 SelfInline,
554}
555
556#[derive(
559 Animate,
560 Clone,
561 ComputeSquaredDistance,
562 Debug,
563 MallocSizeOf,
564 PartialEq,
565 ToCss,
566 ToShmem,
567 ToAnimatedValue,
568 ToAnimatedZero,
569 ToComputedValue,
570 ToResolvedValue,
571)]
572#[repr(C)]
573pub enum GenericMargin<LP> {
574 LengthPercentage(LP),
576 Auto,
578 AnchorSizeFunction(
582 #[animation(field_bound)]
583 #[distance(field_bound)]
584 Box<GenericAnchorSizeFunction<LP>>,
585 ),
586 AnchorContainingCalcFunction(LP),
589}
590
591#[cfg(feature = "servo")]
592impl<LP> GenericMargin<LP> {
593 #[inline]
595 pub fn is_auto(&self) -> bool {
596 matches!(self, Self::Auto)
597 }
598}
599
600#[cfg(feature = "servo")]
601impl GenericMargin<crate::values::computed::LengthPercentage> {
602 #[inline]
604 pub fn is_definitely_zero(&self) -> bool {
605 match self {
606 Self::LengthPercentage(lp) => lp.is_definitely_zero(),
607 _ => false,
608 }
609 }
610}
611
612impl<LP> SpecifiedValueInfo for GenericMargin<LP>
613where
614 LP: SpecifiedValueInfo,
615{
616 fn collect_completion_keywords(f: style_traits::KeywordsCollectFn) {
617 LP::collect_completion_keywords(f);
618 f(&["auto"]);
619 if static_prefs::pref!("layout.css.anchor-positioning.enabled") {
620 f(&["anchor-size"]);
621 }
622 }
623}
624
625impl<LP> Zero for GenericMargin<LP>
626where
627 LP: Zero,
628{
629 fn is_zero(&self) -> bool {
630 match self {
631 Self::LengthPercentage(l) => l.is_zero(),
632 Self::Auto | Self::AnchorSizeFunction(_) | Self::AnchorContainingCalcFunction(_) => {
633 false
634 },
635 }
636 }
637
638 fn zero() -> Self {
639 Self::LengthPercentage(LP::zero())
640 }
641}