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