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)]
163pub enum GenericSize<LengthPercent> {
164 LengthPercentage(LengthPercent),
165 Auto,
166 #[animation(error)]
167 MaxContent,
168 #[animation(error)]
169 MinContent,
170 #[animation(error)]
171 FitContent,
172 #[cfg(feature = "gecko")]
173 #[animation(error)]
174 MozAvailable,
175 #[animation(error)]
176 WebkitFillAvailable,
177 #[animation(error)]
178 Stretch,
179 #[animation(error)]
180 #[css(function = "fit-content")]
181 FitContentFunction(LengthPercent),
182 AnchorSizeFunction(Box<GenericAnchorSizeFunction<Self>>),
183 AnchorContainingCalcFunction(LengthPercent),
184}
185
186impl<LengthPercent> SpecifiedValueInfo for GenericSize<LengthPercent>
187where
188 LengthPercent: SpecifiedValueInfo,
189{
190 fn collect_completion_keywords(f: style_traits::KeywordsCollectFn) {
191 LengthPercent::collect_completion_keywords(f);
192 f(&["auto", "fit-content", "max-content", "min-content"]);
193 if cfg!(feature = "gecko") {
194 f(&["-moz-available"]);
195 }
196 if static_prefs::pref!("layout.css.stretch-size-keyword.enabled") {
197 f(&["stretch"]);
198 }
199 if static_prefs::pref!("layout.css.webkit-fill-available.enabled") {
200 f(&["-webkit-fill-available"]);
201 }
202 if static_prefs::pref!("layout.css.anchor-positioning.enabled") {
203 f(&["anchor-size"]);
204 }
205 }
206}
207
208pub use self::GenericSize as Size;
209
210impl<LengthPercentage> Size<LengthPercentage> {
211 #[inline]
213 pub fn auto() -> Self {
214 Size::Auto
215 }
216
217 #[inline]
219 pub fn is_auto(&self) -> bool {
220 matches!(*self, Size::Auto)
221 }
222}
223
224#[allow(missing_docs)]
226#[derive(
227 Animate,
228 Clone,
229 ComputeSquaredDistance,
230 Debug,
231 MallocSizeOf,
232 PartialEq,
233 ToAnimatedValue,
234 ToAnimatedZero,
235 ToComputedValue,
236 ToCss,
237 ToResolvedValue,
238 ToShmem,
239 ToTyped,
240)]
241#[repr(C, u8)]
242pub enum GenericMaxSize<LengthPercent> {
243 LengthPercentage(LengthPercent),
244 None,
245 #[animation(error)]
246 MaxContent,
247 #[animation(error)]
248 MinContent,
249 #[animation(error)]
250 FitContent,
251 #[cfg(feature = "gecko")]
252 #[animation(error)]
253 MozAvailable,
254 #[animation(error)]
255 WebkitFillAvailable,
256 #[animation(error)]
257 Stretch,
258 #[animation(error)]
259 #[css(function = "fit-content")]
260 FitContentFunction(LengthPercent),
261 AnchorSizeFunction(Box<GenericAnchorSizeFunction<Self>>),
262 AnchorContainingCalcFunction(LengthPercent),
263}
264
265impl<LP> SpecifiedValueInfo for GenericMaxSize<LP>
266where
267 LP: SpecifiedValueInfo,
268{
269 fn collect_completion_keywords(f: style_traits::KeywordsCollectFn) {
270 LP::collect_completion_keywords(f);
271 f(&["none", "fit-content", "max-content", "min-content"]);
272 if cfg!(feature = "gecko") {
273 f(&["-moz-available"]);
274 }
275 if static_prefs::pref!("layout.css.stretch-size-keyword.enabled") {
276 f(&["stretch"]);
277 }
278 if static_prefs::pref!("layout.css.webkit-fill-available.enabled") {
279 f(&["-webkit-fill-available"]);
280 }
281 if static_prefs::pref!("layout.css.anchor-positioning.enabled") {
282 f(&["anchor-size"]);
283 }
284 }
285}
286
287pub use self::GenericMaxSize as MaxSize;
288
289impl<LengthPercentage> MaxSize<LengthPercentage> {
290 #[inline]
292 pub fn none() -> Self {
293 MaxSize::None
294 }
295}
296
297#[derive(
299 Animate,
300 Clone,
301 ComputeSquaredDistance,
302 Copy,
303 Debug,
304 MallocSizeOf,
305 Parse,
306 PartialEq,
307 SpecifiedValueInfo,
308 ToAnimatedValue,
309 ToAnimatedZero,
310 ToComputedValue,
311 ToCss,
312 ToResolvedValue,
313 ToShmem,
314 ToTyped,
315)]
316#[repr(C, u8)]
317pub enum GenericLengthOrNumber<L, N> {
318 Number(N),
323 Length(L),
325}
326
327pub use self::GenericLengthOrNumber as LengthOrNumber;
328
329impl<L, N: Zero> Zero for LengthOrNumber<L, N> {
330 fn zero() -> Self {
331 LengthOrNumber::Number(Zero::zero())
332 }
333
334 fn is_zero(&self) -> bool {
335 match *self {
336 LengthOrNumber::Number(ref n) => n.is_zero(),
337 LengthOrNumber::Length(..) => false,
338 }
339 }
340}
341
342#[derive(
344 Animate,
345 Clone,
346 ComputeSquaredDistance,
347 Copy,
348 Debug,
349 MallocSizeOf,
350 Parse,
351 PartialEq,
352 SpecifiedValueInfo,
353 ToAnimatedValue,
354 ToAnimatedZero,
355 ToComputedValue,
356 ToCss,
357 ToResolvedValue,
358 ToShmem,
359 ToTyped,
360)]
361#[repr(C, u8)]
362#[allow(missing_docs)]
363pub enum GenericLengthPercentageOrNormal<LengthPercent> {
364 LengthPercentage(LengthPercent),
365 Normal,
366}
367
368pub use self::GenericLengthPercentageOrNormal as LengthPercentageOrNormal;
369
370impl<LengthPercent> LengthPercentageOrNormal<LengthPercent> {
371 #[inline]
373 pub fn normal() -> Self {
374 LengthPercentageOrNormal::Normal
375 }
376}
377
378#[derive(
383 Animate,
384 Clone,
385 ComputeSquaredDistance,
386 Debug,
387 MallocSizeOf,
388 PartialEq,
389 SpecifiedValueInfo,
390 ToShmem,
391 ToAnimatedValue,
392 ToAnimatedZero,
393 ToComputedValue,
394 ToResolvedValue,
395 Serialize,
396 Deserialize,
397)]
398#[repr(C)]
399pub struct GenericAnchorSizeFunction<Fallback> {
400 #[animation(constant)]
403 pub target_element: TreeScoped<DashedIdent>,
404 pub size: AnchorSizeKeyword,
407 pub fallback: Optional<Fallback>,
409}
410
411impl<Fallback: TryTacticAdjustment> TryTacticAdjustment for GenericAnchorSizeFunction<Fallback> {
412 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
413 self.size.try_tactic_adjustment(old_side, new_side);
414 if let Some(fallback) = self.fallback.as_mut() {
415 fallback.try_tactic_adjustment(old_side, new_side);
416 }
417 }
418}
419
420impl<Fallback> ToCss for GenericAnchorSizeFunction<Fallback>
421where
422 Fallback: ToCss,
423{
424 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> std::fmt::Result
425 where
426 W: Write,
427 {
428 dest.write_str("anchor-size(")?;
429 let mut previous_entry_printed = false;
430 if !self.target_element.value.0.is_empty() {
431 previous_entry_printed = true;
432 self.target_element.to_css(dest)?;
433 }
434 if self.size != AnchorSizeKeyword::None {
435 if previous_entry_printed {
436 dest.write_str(" ")?;
437 }
438 previous_entry_printed = true;
439 self.size.to_css(dest)?;
440 }
441 if let Some(f) = self.fallback.as_ref() {
442 if previous_entry_printed {
443 dest.write_str(", ")?;
444 }
445 f.to_css(dest)?;
446 }
447 dest.write_str(")")
448 }
449}
450
451impl<Fallback> Parse for GenericAnchorSizeFunction<Fallback>
452where
453 Fallback: Parse,
454{
455 fn parse<'i, 't>(
456 context: &ParserContext,
457 input: &mut Parser<'i, 't>,
458 ) -> Result<Self, ParseError<'i>> {
459 if !static_prefs::pref!("layout.css.anchor-positioning.enabled") {
460 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
461 }
462 input.expect_function_matching("anchor-size")?;
463 Self::parse_inner(context, input, |i| Fallback::parse(context, i))
464 }
465}
466impl<Fallback> GenericAnchorSizeFunction<Fallback> {
467 pub fn valid_for(&self, position_property: PositionProperty) -> bool {
469 position_property.is_absolutely_positioned()
470 }
471}
472
473pub enum AnchorResolutionResult<'a, LengthPercentage> {
475 Resolved(LengthPercentage),
477 Fallback(&'a LengthPercentage),
479 Invalid,
481}
482
483impl<'a, LengthPercentage> AnchorResolutionResult<'a, LengthPercentage> {
484 pub fn new_anchor_invalid(fallback: Option<&'a LengthPercentage>) -> Self {
486 if let Some(fb) = fallback {
487 return Self::Fallback(fb);
488 }
489 Self::Invalid
490 }
491}
492
493impl<LengthPercentage> GenericAnchorSizeFunction<LengthPercentage> {
494 pub fn parse_inner<'i, 't, F>(
496 context: &ParserContext,
497 input: &mut Parser<'i, 't>,
498 f: F,
499 ) -> Result<Self, ParseError<'i>>
500 where
501 F: FnOnce(&mut Parser<'i, '_>) -> Result<LengthPercentage, ParseError<'i>>,
502 {
503 input.parse_nested_block(|i| {
504 let mut target_element = i
505 .try_parse(|i| DashedIdent::parse(context, i))
506 .unwrap_or(DashedIdent::empty());
507 let size = i
508 .try_parse(AnchorSizeKeyword::parse)
509 .unwrap_or(AnchorSizeKeyword::None);
510 if target_element.is_empty() {
511 target_element = i
512 .try_parse(|i| DashedIdent::parse(context, i))
513 .unwrap_or(DashedIdent::empty());
514 }
515 let previous_parsed = !target_element.is_empty() || size != AnchorSizeKeyword::None;
516 let fallback = i
517 .try_parse(|i| {
518 if previous_parsed {
519 i.expect_comma()?;
520 }
521 f(i)
522 })
523 .ok();
524 Ok(GenericAnchorSizeFunction {
525 target_element: TreeScoped::with_default_level(target_element),
526 size: size.into(),
527 fallback: fallback.into(),
528 })
529 })
530 }
531}
532
533#[derive(
535 Animate,
536 Clone,
537 ComputeSquaredDistance,
538 Copy,
539 Debug,
540 MallocSizeOf,
541 PartialEq,
542 Parse,
543 SpecifiedValueInfo,
544 ToCss,
545 ToShmem,
546 ToAnimatedValue,
547 ToAnimatedZero,
548 ToComputedValue,
549 ToResolvedValue,
550 Serialize,
551 Deserialize,
552)]
553#[repr(u8)]
554pub enum AnchorSizeKeyword {
555 #[css(skip)]
557 None,
558 Width,
560 Height,
562 Block,
564 Inline,
566 SelfBlock,
568 SelfInline,
570}
571
572impl TryTacticAdjustment for AnchorSizeKeyword {
573 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
574 if old_side.parallel_to(new_side) {
575 return;
576 }
577 *self = match *self {
578 Self::None => Self::None,
579 Self::Width => Self::Height,
580 Self::Height => Self::Width,
581 Self::Block => Self::Inline,
582 Self::Inline => Self::Block,
583 Self::SelfBlock => Self::SelfInline,
584 Self::SelfInline => Self::SelfBlock,
585 }
586 }
587}
588
589#[derive(
592 Animate,
593 Clone,
594 ComputeSquaredDistance,
595 Debug,
596 MallocSizeOf,
597 PartialEq,
598 ToCss,
599 ToShmem,
600 ToAnimatedValue,
601 ToAnimatedZero,
602 ToComputedValue,
603 ToResolvedValue,
604 ToTyped,
605)]
606#[repr(C)]
607pub enum GenericMargin<LP> {
608 LengthPercentage(LP),
610 Auto,
612 AnchorSizeFunction(Box<GenericAnchorSizeFunction<Self>>),
616 AnchorContainingCalcFunction(LP),
619}
620
621#[cfg(feature = "servo")]
622impl<LP> GenericMargin<LP> {
623 #[inline]
625 pub fn is_auto(&self) -> bool {
626 matches!(self, Self::Auto)
627 }
628}
629
630impl<LP> SpecifiedValueInfo for GenericMargin<LP>
631where
632 LP: SpecifiedValueInfo,
633{
634 fn collect_completion_keywords(f: style_traits::KeywordsCollectFn) {
635 LP::collect_completion_keywords(f);
636 f(&["auto"]);
637 if static_prefs::pref!("layout.css.anchor-positioning.enabled") {
638 f(&["anchor-size"]);
639 }
640 }
641}
642
643impl<LP> Zero for GenericMargin<LP>
644where
645 LP: Zero,
646{
647 fn is_zero(&self) -> bool {
648 match self {
649 Self::LengthPercentage(l) => l.is_zero(),
650 Self::Auto | Self::AnchorSizeFunction(_) | Self::AnchorContainingCalcFunction(_) => {
651 false
652 },
653 }
654 }
655
656 fn zero() -> Self {
657 Self::LengthPercentage(LP::zero())
658 }
659}