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