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
32pub trait IsTreeScoped {
36 fn is_tree_scoped(&self) -> bool {
39 true
40 }
41}
42
43#[repr(C)]
46#[derive(
47 Clone,
48 Copy,
49 Debug,
50 MallocSizeOf,
51 SpecifiedValueInfo,
52 ToAnimatedValue,
53 ToCss,
54 ToResolvedValue,
55 ToShmem,
56 ToTyped,
57 Serialize,
58 Deserialize,
59)]
60#[typed_value(derive_fields)]
61pub struct TreeScoped<T> {
62 pub value: T,
64 #[css(skip)]
66 pub scope: CascadeLevel,
67}
68
69impl<T: IsTreeScoped + PartialEq> PartialEq for TreeScoped<T> {
70 fn eq(&self, other: &Self) -> bool {
71 let tree_scoped = self.value.is_tree_scoped();
72 if tree_scoped != other.value.is_tree_scoped() {
73 return false;
75 }
76 let scopes_equal = self.scope == other.scope;
77 if !scopes_equal && tree_scoped {
78 return false;
80 }
81 self.value == other.value
83 }
84}
85
86impl<T> TreeScoped<T> {
87 pub fn new(value: T, scope: CascadeLevel) -> Self {
89 Self { value, scope }
90 }
91
92 pub fn with_default_level(value: T) -> Self {
95 Self {
96 value,
97 scope: CascadeLevel::same_tree_author_normal(),
98 }
99 }
100}
101
102impl<T> Parse for TreeScoped<T>
103where
104 T: Parse,
105{
106 fn parse<'i, 't>(
107 context: &ParserContext,
108 input: &mut Parser<'i, 't>,
109 ) -> Result<Self, ParseError<'i>> {
110 Ok(TreeScoped {
111 value: T::parse(context, input)?,
112 scope: CascadeLevel::same_tree_author_normal(),
113 })
114 }
115}
116
117impl<T> ToComputedValue for TreeScoped<T>
118where
119 T: ToComputedValue + IsTreeScoped,
120 T::ComputedValue: IsTreeScoped,
121{
122 type ComputedValue = TreeScoped<T::ComputedValue>;
123 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
124 TreeScoped {
125 value: self.value.to_computed_value(context),
126 scope: if context.current_scope().is_tree() {
127 context.current_scope()
128 } else {
129 self.scope.clone()
130 },
131 }
132 }
133
134 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
135 Self {
136 value: ToComputedValue::from_computed_value(&computed.value),
137 scope: computed.scope.clone(),
138 }
139 }
140}
141
142#[derive(
144 Animate,
145 Clone,
146 ComputeSquaredDistance,
147 Copy,
148 Debug,
149 Deserialize,
150 MallocSizeOf,
151 PartialEq,
152 Serialize,
153 SpecifiedValueInfo,
154 ToAnimatedValue,
155 ToAnimatedZero,
156 ToComputedValue,
157 ToResolvedValue,
158 ToShmem,
159 ToTyped,
160)]
161#[repr(C)]
162pub struct GenericPosition<H, V> {
163 pub horizontal: H,
165 pub vertical: V,
167}
168
169impl<H, V> PositionComponent for Position<H, V>
170where
171 H: PositionComponent,
172 V: PositionComponent,
173{
174 #[inline]
175 fn is_center(&self) -> bool {
176 self.horizontal.is_center() && self.vertical.is_center()
177 }
178}
179
180pub use self::GenericPosition as Position;
181
182impl<H, V> Position<H, V> {
183 pub fn new(horizontal: H, vertical: V) -> Self {
185 Self {
186 horizontal,
187 vertical,
188 }
189 }
190}
191
192pub trait PositionComponent {
194 fn is_center(&self) -> bool;
197}
198
199#[derive(
203 Animate,
204 Clone,
205 ComputeSquaredDistance,
206 Copy,
207 Debug,
208 Deserialize,
209 MallocSizeOf,
210 Parse,
211 PartialEq,
212 Serialize,
213 SpecifiedValueInfo,
214 ToAnimatedZero,
215 ToAnimatedValue,
216 ToComputedValue,
217 ToCss,
218 ToResolvedValue,
219 ToShmem,
220 ToTyped,
221)]
222#[repr(C, u8)]
223pub enum GenericPositionOrAuto<Pos> {
224 Position(Pos),
226 Auto,
228}
229
230pub use self::GenericPositionOrAuto as PositionOrAuto;
231
232impl<Pos> PositionOrAuto<Pos> {
233 #[inline]
235 pub fn auto() -> Self {
236 PositionOrAuto::Auto
237 }
238
239 #[inline]
241 pub fn is_auto(&self) -> bool {
242 matches!(self, PositionOrAuto::Auto)
243 }
244}
245
246#[derive(
248 Animate,
249 Clone,
250 ComputeSquaredDistance,
251 Copy,
252 Debug,
253 MallocSizeOf,
254 PartialEq,
255 Parse,
256 SpecifiedValueInfo,
257 ToAnimatedValue,
258 ToAnimatedZero,
259 ToComputedValue,
260 ToCss,
261 ToResolvedValue,
262 ToShmem,
263 ToTyped,
264)]
265#[repr(C, u8)]
266pub enum GenericZIndex<I> {
267 Integer(I),
269 Auto,
271}
272
273pub use self::GenericZIndex as ZIndex;
274
275impl<Integer> ZIndex<Integer> {
276 #[inline]
278 pub fn auto() -> Self {
279 ZIndex::Auto
280 }
281
282 #[inline]
284 pub fn is_auto(self) -> bool {
285 matches!(self, ZIndex::Auto)
286 }
287
288 #[inline]
290 pub fn integer_or(self, auto: Integer) -> Integer {
291 match self {
292 ZIndex::Integer(n) => n,
293 ZIndex::Auto => auto,
294 }
295 }
296}
297
298#[derive(
300 Animate,
301 Clone,
302 ComputeSquaredDistance,
303 Copy,
304 Debug,
305 MallocSizeOf,
306 PartialEq,
307 SpecifiedValueInfo,
308 ToAnimatedValue,
309 ToComputedValue,
310 ToCss,
311 ToResolvedValue,
312 ToShmem,
313)]
314#[repr(C, u8)]
315pub enum PreferredRatio<N> {
316 #[css(skip)]
318 None,
319 Ratio(
321 #[animation(field_bound)]
322 #[css(field_bound)]
323 #[distance(field_bound)]
324 Ratio<N>,
325 ),
326}
327
328#[derive(
330 Animate,
331 Clone,
332 ComputeSquaredDistance,
333 Copy,
334 Debug,
335 MallocSizeOf,
336 PartialEq,
337 SpecifiedValueInfo,
338 ToAnimatedValue,
339 ToComputedValue,
340 ToCss,
341 ToResolvedValue,
342 ToShmem,
343 ToTyped,
344)]
345#[repr(C)]
346pub struct GenericAspectRatio<N> {
347 #[animation(constant)]
349 #[css(represents_keyword)]
350 pub auto: bool,
351 #[animation(field_bound)]
353 #[css(field_bound)]
354 #[distance(field_bound)]
355 pub ratio: PreferredRatio<N>,
356}
357
358pub use self::GenericAspectRatio as AspectRatio;
359
360impl<N> AspectRatio<N> {
361 #[inline]
363 pub fn auto() -> Self {
364 AspectRatio {
365 auto: true,
366 ratio: PreferredRatio::None,
367 }
368 }
369}
370
371impl<N> ToAnimatedZero for AspectRatio<N> {
372 #[inline]
373 fn to_animated_zero(&self) -> Result<Self, ()> {
374 Err(())
375 }
376}
377
378#[derive(
387 Animate,
388 Clone,
389 ComputeSquaredDistance,
390 Debug,
391 MallocSizeOf,
392 PartialEq,
393 ToCss,
394 ToShmem,
395 ToAnimatedValue,
396 ToAnimatedZero,
397 ToComputedValue,
398 ToResolvedValue,
399 ToTyped,
400)]
401#[repr(C)]
402#[typed_value(derive_fields)]
403pub enum GenericInset<P, LP> {
404 LengthPercentage(LP),
406 Auto,
408 AnchorFunction(Box<GenericAnchorFunction<P, Self>>),
412 AnchorSizeFunction(Box<GenericAnchorSizeFunction<Self>>),
416 AnchorContainingCalcFunction(LP),
419}
420
421impl<P, LP> SpecifiedValueInfo for GenericInset<P, LP>
422where
423 LP: SpecifiedValueInfo,
424{
425 fn collect_completion_keywords(f: style_traits::KeywordsCollectFn) {
426 LP::collect_completion_keywords(f);
427 f(&["auto"]);
428 if static_prefs::pref!("layout.css.anchor-positioning.enabled") {
429 f(&["anchor", "anchor-size"]);
430 }
431 }
432}
433
434impl<P, LP> GenericInset<P, LP> {
435 #[inline]
437 pub fn auto() -> Self {
438 Self::Auto
439 }
440
441 #[inline]
443 #[cfg(feature = "servo")]
444 pub fn is_auto(&self) -> bool {
445 matches!(self, Self::Auto)
446 }
447}
448
449pub use self::GenericInset as Inset;
450
451#[derive(
456 Animate,
457 Clone,
458 ComputeSquaredDistance,
459 Debug,
460 MallocSizeOf,
461 PartialEq,
462 SpecifiedValueInfo,
463 ToShmem,
464 ToAnimatedValue,
465 ToAnimatedZero,
466 ToComputedValue,
467 ToResolvedValue,
468 Serialize,
469 Deserialize,
470 ToTyped,
471)]
472#[repr(C)]
473pub struct GenericAnchorFunction<Percentage, Fallback> {
474 #[animation(constant)]
479 pub target_element: TreeScoped<DashedIdent>,
480 pub side: GenericAnchorSide<Percentage>,
483 pub fallback: Optional<Fallback>,
485}
486
487impl<Percentage, Fallback> ToCss for GenericAnchorFunction<Percentage, Fallback>
488where
489 Percentage: ToCss,
490 Fallback: ToCss,
491{
492 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> std::fmt::Result
493 where
494 W: Write,
495 {
496 dest.write_str("anchor(")?;
497 if !self.target_element.value.is_empty() {
498 self.target_element.to_css(dest)?;
499 dest.write_str(" ")?;
500 }
501 self.side.to_css(dest)?;
502 if let Some(f) = self.fallback.as_ref() {
503 dest.write_str(", ")?;
505 f.to_css(dest)?;
506 }
507 dest.write_str(")")
508 }
509}
510
511impl<Percentage, Fallback> GenericAnchorFunction<Percentage, Fallback> {
512 pub fn valid_for(&self, side: PhysicalSide, position_property: PositionProperty) -> bool {
514 position_property.is_absolutely_positioned() && self.side.valid_for(side)
515 }
516}
517
518#[derive(
520 Animate,
521 Clone,
522 ComputeSquaredDistance,
523 Copy,
524 Debug,
525 MallocSizeOf,
526 PartialEq,
527 SpecifiedValueInfo,
528 ToCss,
529 ToShmem,
530 Parse,
531 ToAnimatedValue,
532 ToAnimatedZero,
533 ToComputedValue,
534 ToResolvedValue,
535 Serialize,
536 Deserialize,
537)]
538#[repr(u8)]
539pub enum AnchorSideKeyword {
540 Inside,
542 Outside,
544 Top,
546 Left,
548 Right,
550 Bottom,
552 Start,
556 End,
558 SelfStart,
560 SelfEnd,
562 Center,
564}
565
566impl AnchorSideKeyword {
567 fn from_physical_side(side: PhysicalSide) -> Self {
568 match side {
569 PhysicalSide::Top => Self::Top,
570 PhysicalSide::Right => Self::Right,
571 PhysicalSide::Bottom => Self::Bottom,
572 PhysicalSide::Left => Self::Left,
573 }
574 }
575
576 fn physical_side(self) -> Option<PhysicalSide> {
577 Some(match self {
578 Self::Top => PhysicalSide::Top,
579 Self::Right => PhysicalSide::Right,
580 Self::Bottom => PhysicalSide::Bottom,
581 Self::Left => PhysicalSide::Left,
582 _ => return None,
583 })
584 }
585}
586
587impl TryTacticAdjustment for AnchorSideKeyword {
588 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
589 if !old_side.parallel_to(new_side) {
590 let Some(s) = self.physical_side() else {
591 return;
592 };
593 *self = Self::from_physical_side(if s == new_side {
594 old_side
595 } else if s == old_side {
596 new_side
597 } else if s == new_side.opposite_side() {
598 old_side.opposite_side()
599 } else {
600 debug_assert_eq!(s, old_side.opposite_side());
601 new_side.opposite_side()
602 });
603 return;
604 }
605
606 *self = match self {
607 Self::Center | Self::Inside | Self::Outside => *self,
608 Self::SelfStart => Self::SelfEnd,
609 Self::SelfEnd => Self::SelfStart,
610 Self::Start => Self::End,
611 Self::End => Self::Start,
612 Self::Top => Self::Bottom,
613 Self::Bottom => Self::Top,
614 Self::Left => Self::Right,
615 Self::Right => Self::Left,
616 }
617 }
618}
619
620impl AnchorSideKeyword {
621 fn valid_for(&self, side: PhysicalSide) -> bool {
622 match self {
623 Self::Left | Self::Right => matches!(side, PhysicalSide::Left | PhysicalSide::Right),
624 Self::Top | Self::Bottom => matches!(side, PhysicalSide::Top | PhysicalSide::Bottom),
625 Self::Inside
626 | Self::Outside
627 | Self::Start
628 | Self::End
629 | Self::SelfStart
630 | Self::SelfEnd
631 | Self::Center => true,
632 }
633 }
634}
635
636#[derive(
638 Animate,
639 Clone,
640 ComputeSquaredDistance,
641 Copy,
642 Debug,
643 MallocSizeOf,
644 PartialEq,
645 Parse,
646 SpecifiedValueInfo,
647 ToCss,
648 ToShmem,
649 ToAnimatedValue,
650 ToAnimatedZero,
651 ToComputedValue,
652 ToResolvedValue,
653 Serialize,
654 Deserialize,
655)]
656#[repr(C)]
657pub enum GenericAnchorSide<P> {
658 Keyword(AnchorSideKeyword),
660 Percentage(P),
662}
663
664impl<P> GenericAnchorSide<P> {
665 pub fn valid_for(&self, side: PhysicalSide) -> bool {
667 match self {
668 Self::Keyword(k) => k.valid_for(side),
669 Self::Percentage(_) => true,
670 }
671 }
672}