1use super::{Number, ToComputedValue};
8use crate::derives::*;
9use crate::logical_geometry::PhysicalSide;
10use crate::typed_om::{NumericType, NumericValue, ToTyped, TypedValue, UnitValue};
11use crate::values::animated::{Context as AnimatedContext, ToAnimatedValue};
12use crate::values::computed::position::TryTacticAdjustment;
13use crate::values::computed::{NonNegativeNumber, Percentage, Zoom};
14use crate::values::generics::length::{
15 GenericLengthOrNumber, GenericLengthPercentageOrNormal, GenericMaxSize, GenericSize,
16};
17#[cfg(feature = "gecko")]
18use crate::values::generics::position::TreeScoped;
19use crate::values::generics::NonNegative;
20use crate::values::generics::{length as generics, ClampToNonNegative};
21use crate::values::resolved::{Context as ResolvedContext, ToResolvedValue};
22use crate::values::CSSFloat;
23#[cfg(feature = "gecko")]
24use crate::values::DashedIdent;
25use crate::Zero;
26use app_units::Au;
27use std::fmt::{self, Write};
28use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub, SubAssign};
29use style_traits::{CSSPixel, CssString, CssWriter, ToCss};
30use thin_vec::ThinVec;
31
32pub use super::image::Image;
33pub use super::length_percentage::{LengthPercentage, NonNegativeLengthPercentage};
34pub use crate::values::specified::url::UrlOrNone;
35pub use crate::values::specified::{Angle, BorderStyle, Time};
36
37macro_rules! computed_length_percentage_or_auto {
40 ($inner:ty) => {
41 #[inline]
43 pub fn to_used_value(&self, percentage_basis: Au) -> Option<Au> {
44 match *self {
45 Self::Auto => None,
46 Self::LengthPercentage(ref lp) => Some(lp.to_used_value(percentage_basis)),
47 }
48 }
49 };
50}
51
52pub type LengthPercentageOrAuto = generics::GenericLengthPercentageOrAuto<LengthPercentage>;
54
55impl LengthPercentageOrAuto {
56 pub fn clamp_to_non_negative(self) -> Self {
58 use crate::values::generics::length::LengthPercentageOrAuto::*;
59 match self {
60 LengthPercentage(l) => LengthPercentage(l.clamp_to_non_negative()),
61 Auto => Auto,
62 }
63 }
64
65 pub fn as_ref(&self) -> generics::GenericLengthPercentageOrAuto<&LengthPercentage> {
67 use crate::values::generics::length::LengthPercentageOrAuto::*;
68 match *self {
69 LengthPercentage(ref lp) => LengthPercentage(lp),
70 Auto => Auto,
71 }
72 }
73
74 computed_length_percentage_or_auto!(LengthPercentage);
75}
76
77impl generics::GenericLengthPercentageOrAuto<&LengthPercentage> {
78 #[inline]
80 pub fn percentage_relative_to(&self, basis: Length) -> LengthOrAuto {
81 use crate::values::generics::length::LengthPercentageOrAuto::*;
82 match self {
83 LengthPercentage(length_percentage) => {
84 LengthPercentage(length_percentage.percentage_relative_to(basis))
85 },
86 Auto => Auto,
87 }
88 }
89
90 #[inline]
92 pub fn maybe_percentage_relative_to(&self, basis: Option<Length>) -> LengthOrAuto {
93 use crate::values::generics::length::LengthPercentageOrAuto::*;
94 match self {
95 LengthPercentage(length_percentage) => length_percentage
96 .maybe_percentage_relative_to(basis)
97 .map_or(Auto, LengthPercentage),
98 Auto => Auto,
99 }
100 }
101}
102
103pub type NonNegativeLengthPercentageOrAuto =
105 generics::GenericLengthPercentageOrAuto<NonNegativeLengthPercentage>;
106
107impl NonNegativeLengthPercentageOrAuto {
108 computed_length_percentage_or_auto!(NonNegativeLengthPercentage);
109}
110
111#[derive(
113 Animate,
114 Clone,
115 ComputeSquaredDistance,
116 Copy,
117 Deserialize,
118 MallocSizeOf,
119 PartialEq,
120 PartialOrd,
121 Serialize,
122 ToAnimatedZero,
123 ToComputedValue,
124 ToShmem,
125)]
126#[repr(C)]
127pub struct CSSPixelLength(CSSFloat);
128
129impl ToResolvedValue for CSSPixelLength {
130 type ResolvedValue = Self;
131
132 fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {
133 Self(context.style.effective_zoom.unzoom(self.0))
134 }
135
136 #[inline]
137 fn from_resolved_value(value: Self::ResolvedValue) -> Self {
138 value
139 }
140}
141
142impl ToAnimatedValue for CSSPixelLength {
143 type AnimatedValue = Self;
144
145 fn to_animated_value(self, context: &AnimatedContext) -> Self::AnimatedValue {
146 Self(context.style.effective_zoom.unzoom(self.0))
147 }
148
149 #[inline]
150 fn from_animated_value(value: Self::AnimatedValue) -> Self {
151 value
152 }
153}
154
155impl fmt::Debug for CSSPixelLength {
156 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
157 self.0.fmt(f)?;
158 f.write_str(" px")
159 }
160}
161
162impl CSSPixelLength {
163 #[inline]
165 pub fn new(px: CSSFloat) -> Self {
166 CSSPixelLength(px)
167 }
168
169 #[inline]
171 pub fn normalized(self) -> Self {
172 Self::new(crate::values::normalize(self.0))
173 }
174
175 #[inline]
177 pub fn finite(self) -> Self {
178 Self::new(crate::values::normalize(self.0).min(f32::MAX).max(f32::MIN))
179 }
180
181 #[inline]
183 pub fn scale_by(self, scale: CSSFloat) -> Self {
184 CSSPixelLength(self.0 * scale)
185 }
186
187 #[inline]
189 pub fn px(self) -> CSSFloat {
190 self.0
191 }
192
193 #[inline]
195 pub fn zoom(self, zoom: Zoom) -> Self {
196 Self::new(zoom.zoom(self.px()))
197 }
198
199 #[inline]
201 pub fn to_i32_au(self) -> i32 {
202 Au::from(self).0
203 }
204
205 #[inline]
207 pub fn abs(self) -> Self {
208 CSSPixelLength::new(self.0.abs())
209 }
210
211 #[inline]
213 pub fn clamp_to_non_negative(self) -> Self {
214 CSSPixelLength::new(self.0.max(0.))
215 }
216
217 #[inline]
219 pub fn min(self, other: Self) -> Self {
220 CSSPixelLength::new(self.0.min(other.0))
221 }
222
223 #[inline]
225 pub fn max(self, other: Self) -> Self {
226 CSSPixelLength::new(self.0.max(other.0))
227 }
228
229 #[inline]
231 pub fn max_assign(&mut self, other: Self) {
232 *self = self.max(other);
233 }
234
235 #[inline]
239 pub fn clamp_between_extremums(self, min_size: Self, max_size: Option<Self>) -> Self {
240 self.clamp_below_max(max_size).max(min_size)
241 }
242
243 #[inline]
247 pub fn clamp_below_max(self, max_size: Option<Self>) -> Self {
248 match max_size {
249 None => self,
250 Some(max_size) => self.min(max_size),
251 }
252 }
253}
254
255impl num_traits::Zero for CSSPixelLength {
256 fn zero() -> Self {
257 CSSPixelLength::new(0.)
258 }
259
260 fn is_zero(&self) -> bool {
261 self.px() == 0.
262 }
263}
264
265impl ToCss for CSSPixelLength {
266 #[inline]
267 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
268 where
269 W: Write,
270 {
271 self.0.to_css(dest)?;
272 dest.write_str("px")
273 }
274}
275
276impl ToTyped for CSSPixelLength {
277 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
278 dest.push(TypedValue::Numeric(NumericValue::Unit(UnitValue {
279 numeric_type: NumericType::length(),
280 value: self.0 as f32,
281 unit: CssString::from("px"),
282 })));
283 Ok(())
284 }
285}
286
287impl std::iter::Sum for CSSPixelLength {
288 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
289 iter.fold(Length::zero(), Add::add)
290 }
291}
292
293impl Add for CSSPixelLength {
294 type Output = Self;
295
296 #[inline]
297 fn add(self, other: Self) -> Self {
298 Self::new(self.px() + other.px())
299 }
300}
301
302impl AddAssign for CSSPixelLength {
303 #[inline]
304 fn add_assign(&mut self, other: Self) {
305 self.0 += other.0;
306 }
307}
308
309impl Div for CSSPixelLength {
310 type Output = CSSFloat;
311
312 #[inline]
313 fn div(self, other: Self) -> CSSFloat {
314 self.px() / other.px()
315 }
316}
317
318impl Div<CSSFloat> for CSSPixelLength {
319 type Output = Self;
320
321 #[inline]
322 fn div(self, other: CSSFloat) -> Self {
323 Self::new(self.px() / other)
324 }
325}
326
327impl MulAssign<CSSFloat> for CSSPixelLength {
328 #[inline]
329 fn mul_assign(&mut self, other: CSSFloat) {
330 self.0 *= other;
331 }
332}
333
334impl Mul<CSSFloat> for CSSPixelLength {
335 type Output = Self;
336
337 #[inline]
338 fn mul(self, other: CSSFloat) -> Self {
339 Self::new(self.px() * other)
340 }
341}
342
343impl Neg for CSSPixelLength {
344 type Output = Self;
345
346 #[inline]
347 fn neg(self) -> Self {
348 CSSPixelLength::new(-self.0)
349 }
350}
351
352impl Sub for CSSPixelLength {
353 type Output = Self;
354
355 #[inline]
356 fn sub(self, other: Self) -> Self {
357 Self::new(self.px() - other.px())
358 }
359}
360
361impl SubAssign for CSSPixelLength {
362 #[inline]
363 fn sub_assign(&mut self, other: Self) {
364 self.0 -= other.0;
365 }
366}
367
368impl From<CSSPixelLength> for Au {
369 #[inline]
370 fn from(len: CSSPixelLength) -> Self {
371 Au::from_f32_px(len.0)
372 }
373}
374
375impl From<Au> for CSSPixelLength {
376 #[inline]
377 fn from(len: Au) -> Self {
378 CSSPixelLength::new(len.to_f32_px())
379 }
380}
381
382impl From<CSSPixelLength> for euclid::Length<CSSFloat, CSSPixel> {
383 #[inline]
384 fn from(length: CSSPixelLength) -> Self {
385 Self::new(length.0)
386 }
387}
388
389pub type Length = CSSPixelLength;
391
392pub type LengthOrAuto = generics::GenericLengthPercentageOrAuto<Length>;
394
395pub type NonNegativeLengthOrAuto = generics::GenericLengthPercentageOrAuto<NonNegativeLength>;
397
398pub type LengthOrNumber = GenericLengthOrNumber<Length, Number>;
400
401pub type NonNegativeLength = NonNegative<Length>;
403
404impl ClampToNonNegative for Length {
405 fn clamp_to_non_negative(self) -> Self {
406 Self::new(self.px().max(0.))
407 }
408}
409
410impl NonNegativeLength {
411 #[inline]
413 pub fn new(px: CSSFloat) -> Self {
414 NonNegative(Length::new(px.max(0.)))
415 }
416
417 #[inline]
419 pub fn px(&self) -> CSSFloat {
420 self.0.px()
421 }
422
423 #[inline]
424 pub fn clamp(self) -> Self {
426 if (self.0).0 < 0. {
427 Self::zero()
428 } else {
429 self
430 }
431 }
432}
433
434impl From<Length> for NonNegativeLength {
435 #[inline]
436 fn from(len: Length) -> Self {
437 NonNegative(len)
438 }
439}
440
441impl From<Au> for NonNegativeLength {
442 #[inline]
443 fn from(au: Au) -> Self {
444 NonNegative(au.into())
445 }
446}
447
448impl From<NonNegativeLength> for Au {
449 #[inline]
450 fn from(non_negative_len: NonNegativeLength) -> Self {
451 Au::from(non_negative_len.0)
452 }
453}
454
455pub type NonNegativeLengthPercentageOrNormal =
457 GenericLengthPercentageOrNormal<NonNegativeLengthPercentage>;
458
459pub type NonNegativeLengthOrNumber = GenericLengthOrNumber<NonNegativeLength, NonNegativeNumber>;
461
462pub type Size = GenericSize<NonNegativeLengthPercentage>;
464
465pub type MaxSize = GenericMaxSize<NonNegativeLengthPercentage>;
467
468#[cfg(feature = "gecko")]
469use crate::{
470 gecko_bindings::structs::AnchorPosResolutionParams, logical_geometry::PhysicalAxis,
471 values::generics::length::AnchorSizeKeyword,
472};
473
474#[cfg(feature = "gecko")]
478pub fn resolve_anchor_size(
479 anchor_name: &TreeScoped<DashedIdent>,
480 prop_axis: PhysicalAxis,
481 anchor_size_keyword: AnchorSizeKeyword,
482 params: &AnchorPosResolutionParams,
483) -> Result<Length, ()> {
484 use crate::gecko_bindings::structs::Gecko_GetAnchorPosSize;
485
486 let mut offset = Length::zero();
487 let valid = unsafe {
488 Gecko_GetAnchorPosSize(
489 params,
490 anchor_name.value.0.as_ptr(),
491 &anchor_name.scope,
492 prop_axis as u8,
493 anchor_size_keyword as u8,
494 &mut offset,
495 )
496 };
497
498 if !valid {
499 return Err(());
500 }
501
502 Ok(offset)
503}
504
505pub type Margin = generics::GenericMargin<LengthPercentage>;
507
508impl TryTacticAdjustment for MaxSize {
509 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
510 debug_assert!(
511 old_side.orthogonal_to(new_side),
512 "Sizes should only change axes"
513 );
514 match self {
515 Self::FitContentFunction(lp)
516 | Self::LengthPercentage(lp)
517 | Self::AnchorContainingCalcFunction(lp) => {
518 lp.try_tactic_adjustment(old_side, new_side);
519 },
520 Self::AnchorSizeFunction(s) => s.try_tactic_adjustment(old_side, new_side),
521 Self::None
522 | Self::MaxContent
523 | Self::MinContent
524 | Self::FitContent
525 | Self::WebkitFillAvailable
526 | Self::Stretch => {},
527 #[cfg(feature = "gecko")]
528 Self::MozAvailable => {},
529 }
530 }
531}
532
533impl TryTacticAdjustment for Size {
534 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
535 debug_assert!(
536 old_side.orthogonal_to(new_side),
537 "Sizes should only change axes"
538 );
539 match self {
540 Self::FitContentFunction(lp)
541 | Self::LengthPercentage(lp)
542 | Self::AnchorContainingCalcFunction(lp) => {
543 lp.try_tactic_adjustment(old_side, new_side);
544 },
545 Self::AnchorSizeFunction(s) => s.try_tactic_adjustment(old_side, new_side),
546 Self::Auto
547 | Self::MaxContent
548 | Self::MinContent
549 | Self::FitContent
550 | Self::WebkitFillAvailable
551 | Self::Stretch => {},
552 #[cfg(feature = "gecko")]
553 Self::MozAvailable => {},
554 }
555 }
556}
557
558impl TryTacticAdjustment for Percentage {
559 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
560 if old_side.parallel_to(new_side) {
561 self.0 = 1.0 - self.0;
562 }
563 }
564}
565
566impl TryTacticAdjustment for Margin {
567 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
568 match self {
569 Self::Auto => {},
570 Self::LengthPercentage(lp) | Self::AnchorContainingCalcFunction(lp) => {
571 lp.try_tactic_adjustment(old_side, new_side)
572 },
573 Self::AnchorSizeFunction(anchor) => anchor.try_tactic_adjustment(old_side, new_side),
574 }
575 }
576}