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(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
106 Ok(TreeScoped {
107 value: T::parse(context, input)?,
108 scope: CascadeLevel::same_tree_author_normal(),
109 })
110 }
111}
112
113impl<T> ToComputedValue for TreeScoped<T>
114where
115 T: ToComputedValue + IsTreeScoped,
116 T::ComputedValue: IsTreeScoped,
117{
118 type ComputedValue = TreeScoped<T::ComputedValue>;
119 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
120 TreeScoped {
121 value: self.value.to_computed_value(context),
122 scope: if context.current_scope().is_tree() {
123 context.current_scope()
124 } else {
125 self.scope
126 },
127 }
128 }
129
130 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
131 Self {
132 value: ToComputedValue::from_computed_value(&computed.value),
133 scope: computed.scope,
134 }
135 }
136}
137
138#[derive(
140 Animate,
141 Clone,
142 ComputeSquaredDistance,
143 Copy,
144 Debug,
145 Deserialize,
146 MallocSizeOf,
147 PartialEq,
148 Serialize,
149 SpecifiedValueInfo,
150 ToAnimatedValue,
151 ToAnimatedZero,
152 ToComputedValue,
153 ToResolvedValue,
154 ToShmem,
155 ToTyped,
156)]
157#[repr(C)]
158pub struct GenericPosition<H, V> {
159 pub horizontal: H,
161 pub vertical: V,
163}
164
165impl<H, V> PositionComponent for Position<H, V>
166where
167 H: PositionComponent,
168 V: PositionComponent,
169{
170 #[inline]
171 fn is_center(&self) -> bool {
172 self.horizontal.is_center() && self.vertical.is_center()
173 }
174}
175
176pub use self::GenericPosition as Position;
177
178impl<H, V> Position<H, V> {
179 pub fn new(horizontal: H, vertical: V) -> Self {
181 Self {
182 horizontal,
183 vertical,
184 }
185 }
186}
187
188pub trait PositionComponent {
190 fn is_center(&self) -> bool;
193}
194
195#[derive(
199 Animate,
200 Clone,
201 ComputeSquaredDistance,
202 Copy,
203 Debug,
204 Deserialize,
205 MallocSizeOf,
206 Parse,
207 PartialEq,
208 Serialize,
209 SpecifiedValueInfo,
210 ToAnimatedZero,
211 ToAnimatedValue,
212 ToComputedValue,
213 ToCss,
214 ToResolvedValue,
215 ToShmem,
216 ToTyped,
217)]
218#[repr(C, u8)]
219pub enum GenericPositionOrAuto<Pos> {
220 Position(Pos),
222 Auto,
224}
225
226pub use self::GenericPositionOrAuto as PositionOrAuto;
227
228impl<Pos> PositionOrAuto<Pos> {
229 #[inline]
231 pub fn auto() -> Self {
232 PositionOrAuto::Auto
233 }
234
235 #[inline]
237 pub fn is_auto(&self) -> bool {
238 matches!(self, PositionOrAuto::Auto)
239 }
240}
241
242#[derive(
244 Animate,
245 Clone,
246 ComputeSquaredDistance,
247 Copy,
248 Debug,
249 MallocSizeOf,
250 PartialEq,
251 Parse,
252 SpecifiedValueInfo,
253 ToAnimatedValue,
254 ToAnimatedZero,
255 ToComputedValue,
256 ToCss,
257 ToResolvedValue,
258 ToShmem,
259 ToTyped,
260)]
261#[repr(C, u8)]
262pub enum GenericZIndex<I> {
263 Integer(I),
265 Auto,
267}
268
269pub use self::GenericZIndex as ZIndex;
270
271impl<Integer> ZIndex<Integer> {
272 #[inline]
274 pub fn auto() -> Self {
275 ZIndex::Auto
276 }
277
278 #[inline]
280 pub fn is_auto(self) -> bool {
281 matches!(self, ZIndex::Auto)
282 }
283
284 #[inline]
286 pub fn integer_or(self, auto: Integer) -> Integer {
287 match self {
288 ZIndex::Integer(n) => n,
289 ZIndex::Auto => auto,
290 }
291 }
292}
293
294#[derive(
296 Animate,
297 Clone,
298 ComputeSquaredDistance,
299 Copy,
300 Debug,
301 MallocSizeOf,
302 PartialEq,
303 SpecifiedValueInfo,
304 ToAnimatedValue,
305 ToComputedValue,
306 ToCss,
307 ToResolvedValue,
308 ToShmem,
309)]
310#[repr(C, u8)]
311pub enum PreferredRatio<N> {
312 #[css(skip)]
314 None,
315 Ratio(
317 #[animation(field_bound)]
318 #[css(field_bound)]
319 #[distance(field_bound)]
320 Ratio<N>,
321 ),
322}
323
324#[derive(
326 Animate,
327 Clone,
328 ComputeSquaredDistance,
329 Copy,
330 Debug,
331 MallocSizeOf,
332 PartialEq,
333 SpecifiedValueInfo,
334 ToAnimatedValue,
335 ToComputedValue,
336 ToCss,
337 ToResolvedValue,
338 ToShmem,
339 ToTyped,
340)]
341#[repr(C)]
342#[typed(todo_derive_fields)]
343pub struct GenericAspectRatio<N> {
344 #[animation(constant)]
346 #[css(represents_keyword)]
347 pub auto: bool,
348 #[animation(field_bound)]
350 #[css(field_bound)]
351 #[distance(field_bound)]
352 pub ratio: PreferredRatio<N>,
353}
354
355pub use self::GenericAspectRatio as AspectRatio;
356
357impl<N> AspectRatio<N> {
358 #[inline]
360 pub fn auto() -> Self {
361 AspectRatio {
362 auto: true,
363 ratio: PreferredRatio::None,
364 }
365 }
366}
367
368impl<N> ToAnimatedZero for AspectRatio<N> {
369 #[inline]
370 fn to_animated_zero(&self) -> Result<Self, ()> {
371 Err(())
372 }
373}
374
375#[derive(
384 Animate,
385 Clone,
386 ComputeSquaredDistance,
387 Debug,
388 MallocSizeOf,
389 PartialEq,
390 ToCss,
391 ToShmem,
392 ToAnimatedValue,
393 ToAnimatedZero,
394 ToComputedValue,
395 ToResolvedValue,
396 ToTyped,
397)]
398#[repr(C)]
399pub enum GenericInset<P, LP> {
400 LengthPercentage(LP),
402 Auto,
404 AnchorFunction(Box<GenericAnchorFunction<P, Self>>),
408 AnchorSizeFunction(Box<GenericAnchorSizeFunction<Self>>),
412 AnchorContainingCalcFunction(LP),
415}
416
417impl<P, LP> SpecifiedValueInfo for GenericInset<P, LP>
418where
419 LP: SpecifiedValueInfo,
420{
421 fn collect_completion_keywords(f: style_traits::KeywordsCollectFn) {
422 LP::collect_completion_keywords(f);
423 f(&["auto"]);
424 if crate::pref!("layout.css.anchor-positioning.enabled", gecko = true) {
425 f(&["anchor", "anchor-size"]);
426 }
427 }
428}
429
430impl<P, LP> GenericInset<P, LP> {
431 #[inline]
433 pub fn auto() -> Self {
434 Self::Auto
435 }
436
437 #[inline]
439 #[cfg(feature = "servo")]
440 pub fn is_auto(&self) -> bool {
441 matches!(self, Self::Auto)
442 }
443}
444
445pub use self::GenericInset as Inset;
446
447#[derive(
452 Animate,
453 Clone,
454 ComputeSquaredDistance,
455 Debug,
456 MallocSizeOf,
457 PartialEq,
458 SpecifiedValueInfo,
459 ToShmem,
460 ToAnimatedValue,
461 ToAnimatedZero,
462 ToComputedValue,
463 ToResolvedValue,
464 Serialize,
465 Deserialize,
466 ToTyped,
467)]
468#[repr(C)]
469#[typed(todo_derive_fields)]
470pub struct GenericAnchorFunction<Percentage, Fallback> {
471 #[animation(constant)]
476 pub target_element: TreeScoped<DashedIdent>,
477 pub side: GenericAnchorSide<Percentage>,
480 pub fallback: Optional<Fallback>,
482}
483
484impl<Percentage, Fallback> ToCss for GenericAnchorFunction<Percentage, Fallback>
485where
486 Percentage: ToCss,
487 Fallback: ToCss,
488{
489 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> std::fmt::Result
490 where
491 W: Write,
492 {
493 dest.write_str("anchor(")?;
494 if !self.target_element.value.is_empty() {
495 self.target_element.to_css(dest)?;
496 dest.write_str(" ")?;
497 }
498 self.side.to_css(dest)?;
499 if let Some(f) = self.fallback.as_ref() {
500 dest.write_str(", ")?;
502 f.to_css(dest)?;
503 }
504 dest.write_str(")")
505 }
506}
507
508impl<Percentage, Fallback> GenericAnchorFunction<Percentage, Fallback> {
509 pub fn valid_for(&self, side: PhysicalSide, position_property: PositionProperty) -> bool {
511 position_property.is_absolutely_positioned() && self.side.valid_for(side)
512 }
513}
514
515#[derive(
517 Animate,
518 Clone,
519 ComputeSquaredDistance,
520 Copy,
521 Debug,
522 MallocSizeOf,
523 PartialEq,
524 SpecifiedValueInfo,
525 ToCss,
526 ToShmem,
527 Parse,
528 ToAnimatedValue,
529 ToAnimatedZero,
530 ToComputedValue,
531 ToResolvedValue,
532 Serialize,
533 Deserialize,
534)]
535#[repr(u8)]
536pub enum AnchorSideKeyword {
537 Inside,
539 Outside,
541 Top,
543 Left,
545 Right,
547 Bottom,
549 Start,
553 End,
555 SelfStart,
557 SelfEnd,
559 Center,
561}
562
563impl AnchorSideKeyword {
564 fn from_physical_side(side: PhysicalSide) -> Self {
565 match side {
566 PhysicalSide::Top => Self::Top,
567 PhysicalSide::Right => Self::Right,
568 PhysicalSide::Bottom => Self::Bottom,
569 PhysicalSide::Left => Self::Left,
570 }
571 }
572
573 fn physical_side(self) -> Option<PhysicalSide> {
574 Some(match self {
575 Self::Top => PhysicalSide::Top,
576 Self::Right => PhysicalSide::Right,
577 Self::Bottom => PhysicalSide::Bottom,
578 Self::Left => PhysicalSide::Left,
579 _ => return None,
580 })
581 }
582}
583
584impl TryTacticAdjustment for AnchorSideKeyword {
585 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
586 if !old_side.parallel_to(new_side) {
587 let Some(s) = self.physical_side() else {
588 return;
589 };
590 *self = Self::from_physical_side(if s == new_side {
591 old_side
592 } else if s == old_side {
593 new_side
594 } else if s == new_side.opposite_side() {
595 old_side.opposite_side()
596 } else {
597 debug_assert_eq!(s, old_side.opposite_side());
598 new_side.opposite_side()
599 });
600 return;
601 }
602
603 *self = match self {
604 Self::Center | Self::Inside | Self::Outside => *self,
605 Self::SelfStart => Self::SelfEnd,
606 Self::SelfEnd => Self::SelfStart,
607 Self::Start => Self::End,
608 Self::End => Self::Start,
609 Self::Top => Self::Bottom,
610 Self::Bottom => Self::Top,
611 Self::Left => Self::Right,
612 Self::Right => Self::Left,
613 }
614 }
615}
616
617impl AnchorSideKeyword {
618 fn valid_for(&self, side: PhysicalSide) -> bool {
619 match self {
620 Self::Left | Self::Right => matches!(side, PhysicalSide::Left | PhysicalSide::Right),
621 Self::Top | Self::Bottom => matches!(side, PhysicalSide::Top | PhysicalSide::Bottom),
622 Self::Inside
623 | Self::Outside
624 | Self::Start
625 | Self::End
626 | Self::SelfStart
627 | Self::SelfEnd
628 | Self::Center => true,
629 }
630 }
631}
632
633#[derive(
635 Animate,
636 Clone,
637 ComputeSquaredDistance,
638 Copy,
639 Debug,
640 MallocSizeOf,
641 PartialEq,
642 Parse,
643 SpecifiedValueInfo,
644 ToCss,
645 ToShmem,
646 ToAnimatedValue,
647 ToAnimatedZero,
648 ToComputedValue,
649 ToResolvedValue,
650 Serialize,
651 Deserialize,
652)]
653#[repr(C)]
654pub enum GenericAnchorSide<P> {
655 Keyword(AnchorSideKeyword),
657 Percentage(P),
659}
660
661impl<P> GenericAnchorSide<P> {
662 pub fn valid_for(&self, side: PhysicalSide) -> bool {
664 match self {
665 Self::Keyword(k) => k.valid_for(side),
666 Self::Percentage(_) => true,
667 }
668 }
669}