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 Deserialize,
33 MallocSizeOf,
34 PartialEq,
35 Serialize,
36 SpecifiedValueInfo,
37 ToAnimatedValue,
38 ToAnimatedZero,
39 ToComputedValue,
40 ToCss,
41 ToResolvedValue,
42 ToShmem,
43 ToTyped,
44)]
45#[repr(C, u8)]
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 ToTyped,
398)]
399#[repr(C)]
400#[typed(todo_derive_fields)]
401pub struct GenericAnchorSizeFunction<Fallback> {
402 #[animation(constant)]
405 pub target_element: TreeScoped<DashedIdent>,
406 pub size: AnchorSizeKeyword,
409 pub fallback: Optional<Fallback>,
411}
412
413impl<Fallback: TryTacticAdjustment> TryTacticAdjustment for GenericAnchorSizeFunction<Fallback> {
414 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
415 self.size.try_tactic_adjustment(old_side, new_side);
416 if let Some(fallback) = self.fallback.as_mut() {
417 fallback.try_tactic_adjustment(old_side, new_side);
418 }
419 }
420}
421
422impl<Fallback> ToCss for GenericAnchorSizeFunction<Fallback>
423where
424 Fallback: ToCss,
425{
426 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> std::fmt::Result
427 where
428 W: Write,
429 {
430 dest.write_str("anchor-size(")?;
431 let mut previous_entry_printed = false;
432 if !self.target_element.value.0.is_empty() {
433 previous_entry_printed = true;
434 self.target_element.to_css(dest)?;
435 }
436 if self.size != AnchorSizeKeyword::None {
437 if previous_entry_printed {
438 dest.write_str(" ")?;
439 }
440 previous_entry_printed = true;
441 self.size.to_css(dest)?;
442 }
443 if let Some(f) = self.fallback.as_ref() {
444 if previous_entry_printed {
445 dest.write_str(", ")?;
446 }
447 f.to_css(dest)?;
448 }
449 dest.write_str(")")
450 }
451}
452
453impl<Fallback> Parse for GenericAnchorSizeFunction<Fallback>
454where
455 Fallback: Parse,
456{
457 fn parse<'i, 't>(
458 context: &ParserContext,
459 input: &mut Parser<'i, 't>,
460 ) -> Result<Self, ParseError<'i>> {
461 if !static_prefs::pref!("layout.css.anchor-positioning.enabled") {
462 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
463 }
464 input.expect_function_matching("anchor-size")?;
465 Self::parse_inner(context, input, |i| Fallback::parse(context, i))
466 }
467}
468impl<Fallback> GenericAnchorSizeFunction<Fallback> {
469 pub fn valid_for(&self, position_property: PositionProperty) -> bool {
471 position_property.is_absolutely_positioned()
472 }
473}
474
475pub enum AnchorResolutionResult<'a, LengthPercentage> {
477 Resolved(LengthPercentage),
479 Fallback(&'a LengthPercentage),
481 Invalid,
483}
484
485impl<'a, LengthPercentage> AnchorResolutionResult<'a, LengthPercentage> {
486 pub fn new_anchor_invalid(fallback: Option<&'a LengthPercentage>) -> Self {
488 if let Some(fb) = fallback {
489 return Self::Fallback(fb);
490 }
491 Self::Invalid
492 }
493}
494
495impl<LengthPercentage> GenericAnchorSizeFunction<LengthPercentage> {
496 pub fn parse_inner<'i, 't, F>(
498 context: &ParserContext,
499 input: &mut Parser<'i, 't>,
500 f: F,
501 ) -> Result<Self, ParseError<'i>>
502 where
503 F: FnOnce(&mut Parser<'i, '_>) -> Result<LengthPercentage, ParseError<'i>>,
504 {
505 input.parse_nested_block(|i| {
506 let mut target_element = i
507 .try_parse(|i| DashedIdent::parse(context, i))
508 .unwrap_or(DashedIdent::empty());
509 let size = i
510 .try_parse(AnchorSizeKeyword::parse)
511 .unwrap_or(AnchorSizeKeyword::None);
512 if target_element.is_empty() {
513 target_element = i
514 .try_parse(|i| DashedIdent::parse(context, i))
515 .unwrap_or(DashedIdent::empty());
516 }
517 let previous_parsed = !target_element.is_empty() || size != AnchorSizeKeyword::None;
518 let fallback = i
519 .try_parse(|i| {
520 if previous_parsed {
521 i.expect_comma()?;
522 }
523 f(i)
524 })
525 .ok();
526 Ok(GenericAnchorSizeFunction {
527 target_element: TreeScoped::with_default_level(target_element),
528 size: size.into(),
529 fallback: fallback.into(),
530 })
531 })
532 }
533}
534
535#[derive(
537 Animate,
538 Clone,
539 ComputeSquaredDistance,
540 Copy,
541 Debug,
542 MallocSizeOf,
543 PartialEq,
544 Parse,
545 SpecifiedValueInfo,
546 ToCss,
547 ToShmem,
548 ToAnimatedValue,
549 ToAnimatedZero,
550 ToComputedValue,
551 ToResolvedValue,
552 Serialize,
553 Deserialize,
554)]
555#[repr(u8)]
556pub enum AnchorSizeKeyword {
557 #[css(skip)]
559 None,
560 Width,
562 Height,
564 Block,
566 Inline,
568 SelfBlock,
570 SelfInline,
572}
573
574impl TryTacticAdjustment for AnchorSizeKeyword {
575 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
576 if old_side.parallel_to(new_side) {
577 return;
578 }
579 *self = match *self {
580 Self::None => Self::None,
581 Self::Width => Self::Height,
582 Self::Height => Self::Width,
583 Self::Block => Self::Inline,
584 Self::Inline => Self::Block,
585 Self::SelfBlock => Self::SelfInline,
586 Self::SelfInline => Self::SelfBlock,
587 }
588 }
589}
590
591#[derive(
594 Animate,
595 Clone,
596 ComputeSquaredDistance,
597 Debug,
598 MallocSizeOf,
599 PartialEq,
600 ToCss,
601 ToShmem,
602 ToAnimatedValue,
603 ToAnimatedZero,
604 ToComputedValue,
605 ToResolvedValue,
606 ToTyped,
607)]
608#[repr(C)]
609pub enum GenericMargin<LP> {
610 LengthPercentage(LP),
612 Auto,
614 AnchorSizeFunction(Box<GenericAnchorSizeFunction<Self>>),
618 AnchorContainingCalcFunction(LP),
621}
622
623#[cfg(feature = "servo")]
624impl<LP> GenericMargin<LP> {
625 #[inline]
627 pub fn is_auto(&self) -> bool {
628 matches!(self, Self::Auto)
629 }
630}
631
632impl<LP> SpecifiedValueInfo for GenericMargin<LP>
633where
634 LP: SpecifiedValueInfo,
635{
636 fn collect_completion_keywords(f: style_traits::KeywordsCollectFn) {
637 LP::collect_completion_keywords(f);
638 f(&["auto"]);
639 if static_prefs::pref!("layout.css.anchor-positioning.enabled") {
640 f(&["anchor-size"]);
641 }
642 }
643}
644
645impl<LP> Zero for GenericMargin<LP>
646where
647 LP: Zero,
648{
649 fn is_zero(&self) -> bool {
650 match self {
651 Self::LengthPercentage(l) => l.is_zero(),
652 Self::Auto | Self::AnchorSizeFunction(_) | Self::AnchorContainingCalcFunction(_) => {
653 false
654 },
655 }
656 }
657
658 fn zero() -> Self {
659 Self::LengthPercentage(LP::zero())
660 }
661}