1use cssparser::Parser;
9use std::fmt::Write;
10
11use style_derive::Animate;
12use style_traits::CssWriter;
13use style_traits::ParseError;
14use style_traits::SpecifiedValueInfo;
15use style_traits::ToCss;
16
17use crate::derives::*;
18use crate::logical_geometry::PhysicalSide;
19use crate::parser::{Parse, ParserContext};
20use crate::rule_tree::CascadeLevel;
21use crate::values::animated::ToAnimatedZero;
22use crate::values::computed::position::TryTacticAdjustment;
23use crate::values::generics::box_::PositionProperty;
24use crate::values::generics::length::GenericAnchorSizeFunction;
25use crate::values::generics::ratio::Ratio;
26use crate::values::generics::Optional;
27use crate::values::DashedIdent;
28
29use crate::values::computed::Context;
30use crate::values::computed::ToComputedValue;
31
32#[repr(C)]
35#[derive(
36 Clone,
37 Copy,
38 Debug,
39 MallocSizeOf,
40 PartialEq,
41 SpecifiedValueInfo,
42 ToAnimatedValue,
43 ToCss,
44 ToResolvedValue,
45 ToShmem,
46 ToTyped,
47 Serialize,
48 Deserialize,
49)]
50#[typed_value(derive_fields)]
51pub struct TreeScoped<T> {
52 pub value: T,
54 #[css(skip)]
56 pub scope: CascadeLevel,
57}
58
59impl<T> TreeScoped<T> {
60 pub fn new(value: T, scope: CascadeLevel) -> Self {
62 Self { value, scope }
63 }
64
65 pub fn with_default_level(value: T) -> Self {
68 Self {
69 value,
70 scope: CascadeLevel::same_tree_author_normal(),
71 }
72 }
73}
74
75impl<T> Parse for TreeScoped<T>
76where
77 T: Parse,
78{
79 fn parse<'i, 't>(
80 context: &ParserContext,
81 input: &mut Parser<'i, 't>,
82 ) -> Result<Self, ParseError<'i>> {
83 Ok(TreeScoped {
84 value: T::parse(context, input)?,
85 scope: CascadeLevel::same_tree_author_normal(),
86 })
87 }
88}
89
90impl<T: ToComputedValue> ToComputedValue for TreeScoped<T> {
91 type ComputedValue = TreeScoped<T::ComputedValue>;
92 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
93 TreeScoped {
94 value: self.value.to_computed_value(context),
95 scope: if context.current_scope().is_tree() {
96 context.current_scope()
97 } else {
98 self.scope.clone()
99 },
100 }
101 }
102
103 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
104 Self {
105 value: ToComputedValue::from_computed_value(&computed.value),
106 scope: computed.scope.clone(),
107 }
108 }
109}
110
111#[derive(
113 Animate,
114 Clone,
115 ComputeSquaredDistance,
116 Copy,
117 Debug,
118 Deserialize,
119 MallocSizeOf,
120 PartialEq,
121 Serialize,
122 SpecifiedValueInfo,
123 ToAnimatedValue,
124 ToAnimatedZero,
125 ToComputedValue,
126 ToResolvedValue,
127 ToShmem,
128 ToTyped,
129)]
130#[repr(C)]
131pub struct GenericPosition<H, V> {
132 pub horizontal: H,
134 pub vertical: V,
136}
137
138impl<H, V> PositionComponent for Position<H, V>
139where
140 H: PositionComponent,
141 V: PositionComponent,
142{
143 #[inline]
144 fn is_center(&self) -> bool {
145 self.horizontal.is_center() && self.vertical.is_center()
146 }
147}
148
149pub use self::GenericPosition as Position;
150
151impl<H, V> Position<H, V> {
152 pub fn new(horizontal: H, vertical: V) -> Self {
154 Self {
155 horizontal,
156 vertical,
157 }
158 }
159}
160
161pub trait PositionComponent {
163 fn is_center(&self) -> bool;
166}
167
168#[derive(
172 Animate,
173 Clone,
174 ComputeSquaredDistance,
175 Copy,
176 Debug,
177 Deserialize,
178 MallocSizeOf,
179 Parse,
180 PartialEq,
181 Serialize,
182 SpecifiedValueInfo,
183 ToAnimatedZero,
184 ToAnimatedValue,
185 ToComputedValue,
186 ToCss,
187 ToResolvedValue,
188 ToShmem,
189 ToTyped,
190)]
191#[repr(C, u8)]
192pub enum GenericPositionOrAuto<Pos> {
193 Position(Pos),
195 Auto,
197}
198
199pub use self::GenericPositionOrAuto as PositionOrAuto;
200
201impl<Pos> PositionOrAuto<Pos> {
202 #[inline]
204 pub fn auto() -> Self {
205 PositionOrAuto::Auto
206 }
207
208 #[inline]
210 pub fn is_auto(&self) -> bool {
211 matches!(self, PositionOrAuto::Auto)
212 }
213}
214
215#[derive(
217 Animate,
218 Clone,
219 ComputeSquaredDistance,
220 Copy,
221 Debug,
222 MallocSizeOf,
223 PartialEq,
224 Parse,
225 SpecifiedValueInfo,
226 ToAnimatedValue,
227 ToAnimatedZero,
228 ToComputedValue,
229 ToCss,
230 ToResolvedValue,
231 ToShmem,
232 ToTyped,
233)]
234#[repr(C, u8)]
235pub enum GenericZIndex<I> {
236 Integer(I),
238 Auto,
240}
241
242pub use self::GenericZIndex as ZIndex;
243
244impl<Integer> ZIndex<Integer> {
245 #[inline]
247 pub fn auto() -> Self {
248 ZIndex::Auto
249 }
250
251 #[inline]
253 pub fn is_auto(self) -> bool {
254 matches!(self, ZIndex::Auto)
255 }
256
257 #[inline]
259 pub fn integer_or(self, auto: Integer) -> Integer {
260 match self {
261 ZIndex::Integer(n) => n,
262 ZIndex::Auto => auto,
263 }
264 }
265}
266
267#[derive(
269 Animate,
270 Clone,
271 ComputeSquaredDistance,
272 Copy,
273 Debug,
274 MallocSizeOf,
275 PartialEq,
276 SpecifiedValueInfo,
277 ToAnimatedValue,
278 ToComputedValue,
279 ToCss,
280 ToResolvedValue,
281 ToShmem,
282)]
283#[repr(C, u8)]
284pub enum PreferredRatio<N> {
285 #[css(skip)]
287 None,
288 Ratio(
290 #[animation(field_bound)]
291 #[css(field_bound)]
292 #[distance(field_bound)]
293 Ratio<N>,
294 ),
295}
296
297#[derive(
299 Animate,
300 Clone,
301 ComputeSquaredDistance,
302 Copy,
303 Debug,
304 MallocSizeOf,
305 PartialEq,
306 SpecifiedValueInfo,
307 ToAnimatedValue,
308 ToComputedValue,
309 ToCss,
310 ToResolvedValue,
311 ToShmem,
312 ToTyped,
313)]
314#[repr(C)]
315pub struct GenericAspectRatio<N> {
316 #[animation(constant)]
318 #[css(represents_keyword)]
319 pub auto: bool,
320 #[animation(field_bound)]
322 #[css(field_bound)]
323 #[distance(field_bound)]
324 pub ratio: PreferredRatio<N>,
325}
326
327pub use self::GenericAspectRatio as AspectRatio;
328
329impl<N> AspectRatio<N> {
330 #[inline]
332 pub fn auto() -> Self {
333 AspectRatio {
334 auto: true,
335 ratio: PreferredRatio::None,
336 }
337 }
338}
339
340impl<N> ToAnimatedZero for AspectRatio<N> {
341 #[inline]
342 fn to_animated_zero(&self) -> Result<Self, ()> {
343 Err(())
344 }
345}
346
347#[derive(
356 Animate,
357 Clone,
358 ComputeSquaredDistance,
359 Debug,
360 MallocSizeOf,
361 PartialEq,
362 ToCss,
363 ToShmem,
364 ToAnimatedValue,
365 ToAnimatedZero,
366 ToComputedValue,
367 ToResolvedValue,
368 ToTyped,
369)]
370#[repr(C)]
371#[typed_value(derive_fields)]
372pub enum GenericInset<P, LP> {
373 LengthPercentage(LP),
375 Auto,
377 AnchorFunction(Box<GenericAnchorFunction<P, Self>>),
381 AnchorSizeFunction(Box<GenericAnchorSizeFunction<Self>>),
385 AnchorContainingCalcFunction(LP),
388}
389
390impl<P, LP> SpecifiedValueInfo for GenericInset<P, LP>
391where
392 LP: SpecifiedValueInfo,
393{
394 fn collect_completion_keywords(f: style_traits::KeywordsCollectFn) {
395 LP::collect_completion_keywords(f);
396 f(&["auto"]);
397 if static_prefs::pref!("layout.css.anchor-positioning.enabled") {
398 f(&["anchor", "anchor-size"]);
399 }
400 }
401}
402
403impl<P, LP> GenericInset<P, LP> {
404 #[inline]
406 pub fn auto() -> Self {
407 Self::Auto
408 }
409
410 #[inline]
412 #[cfg(feature = "servo")]
413 pub fn is_auto(&self) -> bool {
414 matches!(self, Self::Auto)
415 }
416}
417
418pub use self::GenericInset as Inset;
419
420#[derive(
425 Animate,
426 Clone,
427 ComputeSquaredDistance,
428 Debug,
429 MallocSizeOf,
430 PartialEq,
431 SpecifiedValueInfo,
432 ToShmem,
433 ToAnimatedValue,
434 ToAnimatedZero,
435 ToComputedValue,
436 ToResolvedValue,
437 Serialize,
438 Deserialize,
439 ToTyped,
440)]
441#[repr(C)]
442pub struct GenericAnchorFunction<Percentage, Fallback> {
443 #[animation(constant)]
448 pub target_element: TreeScoped<DashedIdent>,
449 pub side: GenericAnchorSide<Percentage>,
452 pub fallback: Optional<Fallback>,
454}
455
456impl<Percentage, Fallback> ToCss for GenericAnchorFunction<Percentage, Fallback>
457where
458 Percentage: ToCss,
459 Fallback: ToCss,
460{
461 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> std::fmt::Result
462 where
463 W: Write,
464 {
465 dest.write_str("anchor(")?;
466 if !self.target_element.value.is_empty() {
467 self.target_element.to_css(dest)?;
468 dest.write_str(" ")?;
469 }
470 self.side.to_css(dest)?;
471 if let Some(f) = self.fallback.as_ref() {
472 dest.write_str(", ")?;
474 f.to_css(dest)?;
475 }
476 dest.write_str(")")
477 }
478}
479
480impl<Percentage, Fallback> GenericAnchorFunction<Percentage, Fallback> {
481 pub fn valid_for(&self, side: PhysicalSide, position_property: PositionProperty) -> bool {
483 position_property.is_absolutely_positioned() && self.side.valid_for(side)
484 }
485}
486
487#[derive(
489 Animate,
490 Clone,
491 ComputeSquaredDistance,
492 Copy,
493 Debug,
494 MallocSizeOf,
495 PartialEq,
496 SpecifiedValueInfo,
497 ToCss,
498 ToShmem,
499 Parse,
500 ToAnimatedValue,
501 ToAnimatedZero,
502 ToComputedValue,
503 ToResolvedValue,
504 Serialize,
505 Deserialize,
506)]
507#[repr(u8)]
508pub enum AnchorSideKeyword {
509 Inside,
511 Outside,
513 Top,
515 Left,
517 Right,
519 Bottom,
521 Start,
525 End,
527 SelfStart,
529 SelfEnd,
531 Center,
533}
534
535impl AnchorSideKeyword {
536 fn from_physical_side(side: PhysicalSide) -> Self {
537 match side {
538 PhysicalSide::Top => Self::Top,
539 PhysicalSide::Right => Self::Right,
540 PhysicalSide::Bottom => Self::Bottom,
541 PhysicalSide::Left => Self::Left,
542 }
543 }
544
545 fn physical_side(self) -> Option<PhysicalSide> {
546 Some(match self {
547 Self::Top => PhysicalSide::Top,
548 Self::Right => PhysicalSide::Right,
549 Self::Bottom => PhysicalSide::Bottom,
550 Self::Left => PhysicalSide::Left,
551 _ => return None,
552 })
553 }
554}
555
556impl TryTacticAdjustment for AnchorSideKeyword {
557 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
558 if !old_side.parallel_to(new_side) {
559 let Some(s) = self.physical_side() else {
560 return;
561 };
562 *self = Self::from_physical_side(if s == new_side {
563 old_side
564 } else if s == old_side {
565 new_side
566 } else if s == new_side.opposite_side() {
567 old_side.opposite_side()
568 } else {
569 debug_assert_eq!(s, old_side.opposite_side());
570 new_side.opposite_side()
571 });
572 return;
573 }
574
575 *self = match self {
576 Self::Center | Self::Inside | Self::Outside => *self,
577 Self::SelfStart => Self::SelfEnd,
578 Self::SelfEnd => Self::SelfStart,
579 Self::Start => Self::End,
580 Self::End => Self::Start,
581 Self::Top => Self::Bottom,
582 Self::Bottom => Self::Top,
583 Self::Left => Self::Right,
584 Self::Right => Self::Left,
585 }
586 }
587}
588
589impl AnchorSideKeyword {
590 fn valid_for(&self, side: PhysicalSide) -> bool {
591 match self {
592 Self::Left | Self::Right => matches!(side, PhysicalSide::Left | PhysicalSide::Right),
593 Self::Top | Self::Bottom => matches!(side, PhysicalSide::Top | PhysicalSide::Bottom),
594 Self::Inside
595 | Self::Outside
596 | Self::Start
597 | Self::End
598 | Self::SelfStart
599 | Self::SelfEnd
600 | Self::Center => true,
601 }
602 }
603}
604
605#[derive(
607 Animate,
608 Clone,
609 ComputeSquaredDistance,
610 Copy,
611 Debug,
612 MallocSizeOf,
613 PartialEq,
614 Parse,
615 SpecifiedValueInfo,
616 ToCss,
617 ToShmem,
618 ToAnimatedValue,
619 ToAnimatedZero,
620 ToComputedValue,
621 ToResolvedValue,
622 Serialize,
623 Deserialize,
624)]
625#[repr(C)]
626pub enum GenericAnchorSide<P> {
627 Keyword(AnchorSideKeyword),
629 Percentage(P),
631}
632
633impl<P> GenericAnchorSide<P> {
634 pub fn valid_for(&self, side: PhysicalSide) -> bool {
636 match self {
637 Self::Keyword(k) => k.valid_for(side),
638 Self::Percentage(_) => true,
639 }
640 }
641}