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