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)]
60pub struct TreeScoped<T> {
61 pub value: T,
63 #[css(skip)]
65 pub scope: CascadeLevel,
66}
67
68impl<T: IsTreeScoped + PartialEq> PartialEq for TreeScoped<T> {
69 fn eq(&self, other: &Self) -> bool {
70 let tree_scoped = self.value.is_tree_scoped();
71 if tree_scoped != other.value.is_tree_scoped() {
72 return false;
74 }
75 let scopes_equal = self.scope == other.scope;
76 if !scopes_equal && tree_scoped {
77 return false;
79 }
80 self.value == other.value
82 }
83}
84
85impl<T> TreeScoped<T> {
86 pub fn new(value: T, scope: CascadeLevel) -> Self {
88 Self { value, scope }
89 }
90
91 pub fn with_default_level(value: T) -> Self {
94 Self {
95 value,
96 scope: CascadeLevel::same_tree_author_normal(),
97 }
98 }
99}
100
101impl<T> Parse for TreeScoped<T>
102where
103 T: Parse,
104{
105 fn parse<'i, 't>(
106 context: &ParserContext,
107 input: &mut Parser<'i, 't>,
108 ) -> Result<Self, ParseError<'i>> {
109 Ok(TreeScoped {
110 value: T::parse(context, input)?,
111 scope: CascadeLevel::same_tree_author_normal(),
112 })
113 }
114}
115
116impl<T> ToComputedValue for TreeScoped<T>
117where
118 T: ToComputedValue + IsTreeScoped,
119 T::ComputedValue: IsTreeScoped,
120{
121 type ComputedValue = TreeScoped<T::ComputedValue>;
122 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
123 TreeScoped {
124 value: self.value.to_computed_value(context),
125 scope: if context.current_scope().is_tree() {
126 context.current_scope()
127 } else {
128 self.scope.clone()
129 },
130 }
131 }
132
133 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
134 Self {
135 value: ToComputedValue::from_computed_value(&computed.value),
136 scope: computed.scope.clone(),
137 }
138 }
139}
140
141#[derive(
143 Animate,
144 Clone,
145 ComputeSquaredDistance,
146 Copy,
147 Debug,
148 Deserialize,
149 MallocSizeOf,
150 PartialEq,
151 Serialize,
152 SpecifiedValueInfo,
153 ToAnimatedValue,
154 ToAnimatedZero,
155 ToComputedValue,
156 ToResolvedValue,
157 ToShmem,
158 ToTyped,
159)]
160#[repr(C)]
161pub struct GenericPosition<H, V> {
162 pub horizontal: H,
164 pub vertical: V,
166}
167
168impl<H, V> PositionComponent for Position<H, V>
169where
170 H: PositionComponent,
171 V: PositionComponent,
172{
173 #[inline]
174 fn is_center(&self) -> bool {
175 self.horizontal.is_center() && self.vertical.is_center()
176 }
177}
178
179pub use self::GenericPosition as Position;
180
181impl<H, V> Position<H, V> {
182 pub fn new(horizontal: H, vertical: V) -> Self {
184 Self {
185 horizontal,
186 vertical,
187 }
188 }
189}
190
191pub trait PositionComponent {
193 fn is_center(&self) -> bool;
196}
197
198#[derive(
202 Animate,
203 Clone,
204 ComputeSquaredDistance,
205 Copy,
206 Debug,
207 Deserialize,
208 MallocSizeOf,
209 Parse,
210 PartialEq,
211 Serialize,
212 SpecifiedValueInfo,
213 ToAnimatedZero,
214 ToAnimatedValue,
215 ToComputedValue,
216 ToCss,
217 ToResolvedValue,
218 ToShmem,
219 ToTyped,
220)]
221#[repr(C, u8)]
222pub enum GenericPositionOrAuto<Pos> {
223 Position(Pos),
225 Auto,
227}
228
229pub use self::GenericPositionOrAuto as PositionOrAuto;
230
231impl<Pos> PositionOrAuto<Pos> {
232 #[inline]
234 pub fn auto() -> Self {
235 PositionOrAuto::Auto
236 }
237
238 #[inline]
240 pub fn is_auto(&self) -> bool {
241 matches!(self, PositionOrAuto::Auto)
242 }
243}
244
245#[derive(
247 Animate,
248 Clone,
249 ComputeSquaredDistance,
250 Copy,
251 Debug,
252 MallocSizeOf,
253 PartialEq,
254 Parse,
255 SpecifiedValueInfo,
256 ToAnimatedValue,
257 ToAnimatedZero,
258 ToComputedValue,
259 ToCss,
260 ToResolvedValue,
261 ToShmem,
262 ToTyped,
263)]
264#[repr(C, u8)]
265pub enum GenericZIndex<I> {
266 Integer(I),
268 Auto,
270}
271
272pub use self::GenericZIndex as ZIndex;
273
274impl<Integer> ZIndex<Integer> {
275 #[inline]
277 pub fn auto() -> Self {
278 ZIndex::Auto
279 }
280
281 #[inline]
283 pub fn is_auto(self) -> bool {
284 matches!(self, ZIndex::Auto)
285 }
286
287 #[inline]
289 pub fn integer_or(self, auto: Integer) -> Integer {
290 match self {
291 ZIndex::Integer(n) => n,
292 ZIndex::Auto => auto,
293 }
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)]
313#[repr(C, u8)]
314pub enum PreferredRatio<N> {
315 #[css(skip)]
317 None,
318 Ratio(
320 #[animation(field_bound)]
321 #[css(field_bound)]
322 #[distance(field_bound)]
323 Ratio<N>,
324 ),
325}
326
327#[derive(
329 Animate,
330 Clone,
331 ComputeSquaredDistance,
332 Copy,
333 Debug,
334 MallocSizeOf,
335 PartialEq,
336 SpecifiedValueInfo,
337 ToAnimatedValue,
338 ToComputedValue,
339 ToCss,
340 ToResolvedValue,
341 ToShmem,
342 ToTyped,
343)]
344#[repr(C)]
345#[typed(todo_derive_fields)]
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)]
402pub enum GenericInset<P, LP> {
403 LengthPercentage(LP),
405 Auto,
407 AnchorFunction(Box<GenericAnchorFunction<P, Self>>),
411 AnchorSizeFunction(Box<GenericAnchorSizeFunction<Self>>),
415 AnchorContainingCalcFunction(LP),
418}
419
420impl<P, LP> SpecifiedValueInfo for GenericInset<P, LP>
421where
422 LP: SpecifiedValueInfo,
423{
424 fn collect_completion_keywords(f: style_traits::KeywordsCollectFn) {
425 LP::collect_completion_keywords(f);
426 f(&["auto"]);
427 if static_prefs::pref!("layout.css.anchor-positioning.enabled") {
428 f(&["anchor", "anchor-size"]);
429 }
430 }
431}
432
433impl<P, LP> GenericInset<P, LP> {
434 #[inline]
436 pub fn auto() -> Self {
437 Self::Auto
438 }
439
440 #[inline]
442 #[cfg(feature = "servo")]
443 pub fn is_auto(&self) -> bool {
444 matches!(self, Self::Auto)
445 }
446}
447
448pub use self::GenericInset as Inset;
449
450#[derive(
455 Animate,
456 Clone,
457 ComputeSquaredDistance,
458 Debug,
459 MallocSizeOf,
460 PartialEq,
461 SpecifiedValueInfo,
462 ToShmem,
463 ToAnimatedValue,
464 ToAnimatedZero,
465 ToComputedValue,
466 ToResolvedValue,
467 Serialize,
468 Deserialize,
469 ToTyped,
470)]
471#[repr(C)]
472#[typed(todo_derive_fields)]
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}