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)]
371pub enum GenericInset<P, LP> {
372 LengthPercentage(LP),
374 Auto,
376 AnchorFunction(Box<GenericAnchorFunction<P, Self>>),
380 AnchorSizeFunction(Box<GenericAnchorSizeFunction<Self>>),
384 AnchorContainingCalcFunction(LP),
387}
388
389impl<P, LP> SpecifiedValueInfo for GenericInset<P, LP>
390where
391 LP: SpecifiedValueInfo,
392{
393 fn collect_completion_keywords(f: style_traits::KeywordsCollectFn) {
394 LP::collect_completion_keywords(f);
395 f(&["auto"]);
396 if static_prefs::pref!("layout.css.anchor-positioning.enabled") {
397 f(&["anchor", "anchor-size"]);
398 }
399 }
400}
401
402impl<P, LP> GenericInset<P, LP> {
403 #[inline]
405 pub fn auto() -> Self {
406 Self::Auto
407 }
408
409 #[inline]
411 #[cfg(feature = "servo")]
412 pub fn is_auto(&self) -> bool {
413 matches!(self, Self::Auto)
414 }
415}
416
417pub use self::GenericInset as Inset;
418
419#[derive(
424 Animate,
425 Clone,
426 ComputeSquaredDistance,
427 Debug,
428 MallocSizeOf,
429 PartialEq,
430 SpecifiedValueInfo,
431 ToShmem,
432 ToAnimatedValue,
433 ToAnimatedZero,
434 ToComputedValue,
435 ToResolvedValue,
436 Serialize,
437 Deserialize,
438)]
439#[repr(C)]
440pub struct GenericAnchorFunction<Percentage, Fallback> {
441 #[animation(constant)]
446 pub target_element: TreeScoped<DashedIdent>,
447 pub side: GenericAnchorSide<Percentage>,
450 pub fallback: Optional<Fallback>,
452}
453
454impl<Percentage, Fallback> ToCss for GenericAnchorFunction<Percentage, Fallback>
455where
456 Percentage: ToCss,
457 Fallback: ToCss,
458{
459 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> std::fmt::Result
460 where
461 W: Write,
462 {
463 dest.write_str("anchor(")?;
464 if !self.target_element.value.is_empty() {
465 self.target_element.to_css(dest)?;
466 dest.write_str(" ")?;
467 }
468 self.side.to_css(dest)?;
469 if let Some(f) = self.fallback.as_ref() {
470 dest.write_str(", ")?;
472 f.to_css(dest)?;
473 }
474 dest.write_str(")")
475 }
476}
477
478impl<Percentage, Fallback> GenericAnchorFunction<Percentage, Fallback> {
479 pub fn valid_for(&self, side: PhysicalSide, position_property: PositionProperty) -> bool {
481 position_property.is_absolutely_positioned() && self.side.valid_for(side)
482 }
483}
484
485#[derive(
487 Animate,
488 Clone,
489 ComputeSquaredDistance,
490 Copy,
491 Debug,
492 MallocSizeOf,
493 PartialEq,
494 SpecifiedValueInfo,
495 ToCss,
496 ToShmem,
497 Parse,
498 ToAnimatedValue,
499 ToAnimatedZero,
500 ToComputedValue,
501 ToResolvedValue,
502 Serialize,
503 Deserialize,
504)]
505#[repr(u8)]
506pub enum AnchorSideKeyword {
507 Inside,
509 Outside,
511 Top,
513 Left,
515 Right,
517 Bottom,
519 Start,
523 End,
525 SelfStart,
527 SelfEnd,
529 Center,
531}
532
533impl AnchorSideKeyword {
534 fn from_physical_side(side: PhysicalSide) -> Self {
535 match side {
536 PhysicalSide::Top => Self::Top,
537 PhysicalSide::Right => Self::Right,
538 PhysicalSide::Bottom => Self::Bottom,
539 PhysicalSide::Left => Self::Left,
540 }
541 }
542
543 fn physical_side(self) -> Option<PhysicalSide> {
544 Some(match self {
545 Self::Top => PhysicalSide::Top,
546 Self::Right => PhysicalSide::Right,
547 Self::Bottom => PhysicalSide::Bottom,
548 Self::Left => PhysicalSide::Left,
549 _ => return None,
550 })
551 }
552}
553
554impl TryTacticAdjustment for AnchorSideKeyword {
555 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
556 if !old_side.parallel_to(new_side) {
557 let Some(s) = self.physical_side() else {
558 return;
559 };
560 *self = Self::from_physical_side(if s == new_side {
561 old_side
562 } else if s == old_side {
563 new_side
564 } else if s == new_side.opposite_side() {
565 old_side.opposite_side()
566 } else {
567 debug_assert_eq!(s, old_side.opposite_side());
568 new_side.opposite_side()
569 });
570 return;
571 }
572
573 *self = match self {
574 Self::Center | Self::Inside | Self::Outside => *self,
575 Self::SelfStart => Self::SelfEnd,
576 Self::SelfEnd => Self::SelfStart,
577 Self::Start => Self::End,
578 Self::End => Self::Start,
579 Self::Top => Self::Bottom,
580 Self::Bottom => Self::Top,
581 Self::Left => Self::Right,
582 Self::Right => Self::Left,
583 }
584 }
585}
586
587impl AnchorSideKeyword {
588 fn valid_for(&self, side: PhysicalSide) -> bool {
589 match self {
590 Self::Left | Self::Right => matches!(side, PhysicalSide::Left | PhysicalSide::Right),
591 Self::Top | Self::Bottom => matches!(side, PhysicalSide::Top | PhysicalSide::Bottom),
592 Self::Inside
593 | Self::Outside
594 | Self::Start
595 | Self::End
596 | Self::SelfStart
597 | Self::SelfEnd
598 | Self::Center => true,
599 }
600 }
601}
602
603#[derive(
605 Animate,
606 Clone,
607 ComputeSquaredDistance,
608 Copy,
609 Debug,
610 MallocSizeOf,
611 PartialEq,
612 Parse,
613 SpecifiedValueInfo,
614 ToCss,
615 ToShmem,
616 ToAnimatedValue,
617 ToAnimatedZero,
618 ToComputedValue,
619 ToResolvedValue,
620 Serialize,
621 Deserialize,
622)]
623#[repr(C)]
624pub enum GenericAnchorSide<P> {
625 Keyword(AnchorSideKeyword),
627 Percentage(P),
629}
630
631impl<P> GenericAnchorSide<P> {
632 pub fn valid_for(&self, side: PhysicalSide) -> bool {
634 match self {
635 Self::Keyword(k) => k.valid_for(side),
636 Self::Percentage(_) => true,
637 }
638 }
639}