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