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