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