1use super::{Context, Number, ToComputedValue};
8use crate::derives::*;
9use crate::logical_geometry::PhysicalSide;
10use crate::values::animated::{Context as AnimatedContext, ToAnimatedValue};
11use crate::values::computed::position::TryTacticAdjustment;
12use crate::values::computed::{NonNegativeNumber, Percentage, Zoom};
13use crate::values::generics::length::{
14 GenericLengthOrNumber, GenericLengthPercentageOrNormal, GenericMaxSize, GenericSize,
15};
16#[cfg(feature = "gecko")]
17use crate::values::generics::position::TreeScoped;
18use crate::values::generics::NonNegative;
19use crate::values::generics::{length as generics, ClampToNonNegative};
20use crate::values::resolved::{Context as ResolvedContext, ToResolvedValue};
21use crate::values::specified::length::{AbsoluteLength, FontBaseSize, LineHeightBase};
22#[cfg(feature = "gecko")]
23use crate::values::DashedIdent;
24use crate::values::{specified, CSSFloat};
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, NumericValue, ToCss, ToTyped, TypedValue};
30
31pub use super::image::Image;
32pub use super::length_percentage::{LengthPercentage, NonNegativeLengthPercentage};
33pub use crate::values::specified::url::UrlOrNone;
34pub use crate::values::specified::{Angle, BorderStyle, Time};
35
36impl ToComputedValue for specified::NoCalcLength {
37 type ComputedValue = Length;
38
39 #[inline]
40 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
41 self.to_computed_value_with_base_size(
42 context,
43 FontBaseSize::CurrentStyle,
44 LineHeightBase::CurrentStyle,
45 )
46 }
47
48 #[inline]
49 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
50 Self::Absolute(AbsoluteLength::Px(computed.px()))
51 }
52}
53
54impl specified::NoCalcLength {
55 pub fn to_computed_value_with_base_size(
57 &self,
58 context: &Context,
59 base_size: FontBaseSize,
60 line_height_base: LineHeightBase,
61 ) -> Length {
62 match *self {
63 Self::Absolute(length) => length.to_computed_value(context),
64 Self::FontRelative(length) => {
65 length.to_computed_value(context, base_size, line_height_base)
66 },
67 Self::ViewportPercentage(length) => length.to_computed_value(context),
68 Self::ContainerRelative(length) => length.to_computed_value(context),
69 Self::ServoCharacterWidth(length) => length
70 .to_computed_value(context.style().get_font().clone_font_size().computed_size()),
71 }
72 }
73}
74
75impl ToComputedValue for specified::Length {
76 type ComputedValue = Length;
77
78 #[inline]
79 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
80 match *self {
81 Self::NoCalc(l) => l.to_computed_value(context),
82 Self::Calc(ref calc) => {
83 let result = calc.to_computed_value(context);
84 debug_assert!(
85 result.to_length().is_some(),
86 "{:?} didn't resolve to a length: {:?}",
87 calc,
88 result,
89 );
90 result.to_length().unwrap_or_else(Length::zero)
91 },
92 }
93 }
94
95 #[inline]
96 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
97 Self::NoCalc(specified::NoCalcLength::from_computed_value(computed))
98 }
99}
100
101macro_rules! computed_length_percentage_or_auto {
104 ($inner:ty) => {
105 #[inline]
107 pub fn to_used_value(&self, percentage_basis: Au) -> Option<Au> {
108 match *self {
109 Self::Auto => None,
110 Self::LengthPercentage(ref lp) => Some(lp.to_used_value(percentage_basis)),
111 }
112 }
113 };
114}
115
116pub type LengthPercentageOrAuto = generics::GenericLengthPercentageOrAuto<LengthPercentage>;
118
119impl LengthPercentageOrAuto {
120 pub fn clamp_to_non_negative(self) -> Self {
122 use crate::values::generics::length::LengthPercentageOrAuto::*;
123 match self {
124 LengthPercentage(l) => LengthPercentage(l.clamp_to_non_negative()),
125 Auto => Auto,
126 }
127 }
128
129 pub fn as_ref(&self) -> generics::GenericLengthPercentageOrAuto<&LengthPercentage> {
131 use crate::values::generics::length::LengthPercentageOrAuto::*;
132 match *self {
133 LengthPercentage(ref lp) => LengthPercentage(lp),
134 Auto => Auto,
135 }
136 }
137
138 computed_length_percentage_or_auto!(LengthPercentage);
139}
140
141impl generics::GenericLengthPercentageOrAuto<&LengthPercentage> {
142 #[inline]
144 pub fn percentage_relative_to(&self, basis: Length) -> LengthOrAuto {
145 use crate::values::generics::length::LengthPercentageOrAuto::*;
146 match self {
147 LengthPercentage(length_percentage) => {
148 LengthPercentage(length_percentage.percentage_relative_to(basis))
149 },
150 Auto => Auto,
151 }
152 }
153
154 #[inline]
156 pub fn maybe_percentage_relative_to(&self, basis: Option<Length>) -> LengthOrAuto {
157 use crate::values::generics::length::LengthPercentageOrAuto::*;
158 match self {
159 LengthPercentage(length_percentage) => length_percentage
160 .maybe_percentage_relative_to(basis)
161 .map_or(Auto, LengthPercentage),
162 Auto => Auto,
163 }
164 }
165}
166
167pub type NonNegativeLengthPercentageOrAuto =
169 generics::GenericLengthPercentageOrAuto<NonNegativeLengthPercentage>;
170
171impl NonNegativeLengthPercentageOrAuto {
172 computed_length_percentage_or_auto!(NonNegativeLengthPercentage);
173}
174
175#[derive(
177 Animate,
178 Clone,
179 ComputeSquaredDistance,
180 Copy,
181 Deserialize,
182 MallocSizeOf,
183 PartialEq,
184 PartialOrd,
185 Serialize,
186 ToAnimatedZero,
187 ToComputedValue,
188 ToShmem,
189)]
190#[repr(C)]
191pub struct CSSPixelLength(CSSFloat);
192
193impl ToResolvedValue for CSSPixelLength {
194 type ResolvedValue = Self;
195
196 fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {
197 Self(context.style.effective_zoom.unzoom(self.0))
198 }
199
200 #[inline]
201 fn from_resolved_value(value: Self::ResolvedValue) -> Self {
202 value
203 }
204}
205
206impl ToAnimatedValue for CSSPixelLength {
207 type AnimatedValue = Self;
208
209 fn to_animated_value(self, context: &AnimatedContext) -> Self::AnimatedValue {
210 Self(context.style.effective_zoom.unzoom(self.0))
211 }
212
213 #[inline]
214 fn from_animated_value(value: Self::AnimatedValue) -> Self {
215 value
216 }
217}
218
219impl fmt::Debug for CSSPixelLength {
220 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
221 self.0.fmt(f)?;
222 f.write_str(" px")
223 }
224}
225
226impl CSSPixelLength {
227 #[inline]
229 pub fn new(px: CSSFloat) -> Self {
230 CSSPixelLength(px)
231 }
232
233 #[inline]
235 pub fn normalized(self) -> Self {
236 Self::new(crate::values::normalize(self.0))
237 }
238
239 #[inline]
241 pub fn finite(self) -> Self {
242 Self::new(crate::values::normalize(self.0).min(f32::MAX).max(f32::MIN))
243 }
244
245 #[inline]
247 pub fn scale_by(self, scale: CSSFloat) -> Self {
248 CSSPixelLength(self.0 * scale)
249 }
250
251 #[inline]
253 pub fn px(self) -> CSSFloat {
254 self.0
255 }
256
257 #[inline]
259 pub fn zoom(self, zoom: Zoom) -> Self {
260 Self::new(zoom.zoom(self.px()))
261 }
262
263 #[inline]
265 pub fn to_i32_au(self) -> i32 {
266 Au::from(self).0
267 }
268
269 #[inline]
271 pub fn abs(self) -> Self {
272 CSSPixelLength::new(self.0.abs())
273 }
274
275 #[inline]
277 pub fn clamp_to_non_negative(self) -> Self {
278 CSSPixelLength::new(self.0.max(0.))
279 }
280
281 #[inline]
283 pub fn min(self, other: Self) -> Self {
284 CSSPixelLength::new(self.0.min(other.0))
285 }
286
287 #[inline]
289 pub fn max(self, other: Self) -> Self {
290 CSSPixelLength::new(self.0.max(other.0))
291 }
292
293 #[inline]
295 pub fn max_assign(&mut self, other: Self) {
296 *self = self.max(other);
297 }
298
299 #[inline]
303 pub fn clamp_between_extremums(self, min_size: Self, max_size: Option<Self>) -> Self {
304 self.clamp_below_max(max_size).max(min_size)
305 }
306
307 #[inline]
311 pub fn clamp_below_max(self, max_size: Option<Self>) -> Self {
312 match max_size {
313 None => self,
314 Some(max_size) => self.min(max_size),
315 }
316 }
317}
318
319impl num_traits::Zero for CSSPixelLength {
320 fn zero() -> Self {
321 CSSPixelLength::new(0.)
322 }
323
324 fn is_zero(&self) -> bool {
325 self.px() == 0.
326 }
327}
328
329impl ToCss for CSSPixelLength {
330 #[inline]
331 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
332 where
333 W: Write,
334 {
335 self.0.to_css(dest)?;
336 dest.write_str("px")
337 }
338}
339
340impl ToTyped for CSSPixelLength {
341 fn to_typed(&self) -> Option<TypedValue> {
342 Some(TypedValue::Numeric(NumericValue::Unit {
343 value: self.0 as f32,
344 unit: CssString::from("px"),
345 }))
346 }
347}
348
349impl std::iter::Sum for CSSPixelLength {
350 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
351 iter.fold(Length::zero(), Add::add)
352 }
353}
354
355impl Add for CSSPixelLength {
356 type Output = Self;
357
358 #[inline]
359 fn add(self, other: Self) -> Self {
360 Self::new(self.px() + other.px())
361 }
362}
363
364impl AddAssign for CSSPixelLength {
365 #[inline]
366 fn add_assign(&mut self, other: Self) {
367 self.0 += other.0;
368 }
369}
370
371impl Div for CSSPixelLength {
372 type Output = CSSFloat;
373
374 #[inline]
375 fn div(self, other: Self) -> CSSFloat {
376 self.px() / other.px()
377 }
378}
379
380impl Div<CSSFloat> for CSSPixelLength {
381 type Output = Self;
382
383 #[inline]
384 fn div(self, other: CSSFloat) -> Self {
385 Self::new(self.px() / other)
386 }
387}
388
389impl MulAssign<CSSFloat> for CSSPixelLength {
390 #[inline]
391 fn mul_assign(&mut self, other: CSSFloat) {
392 self.0 *= other;
393 }
394}
395
396impl Mul<CSSFloat> for CSSPixelLength {
397 type Output = Self;
398
399 #[inline]
400 fn mul(self, other: CSSFloat) -> Self {
401 Self::new(self.px() * other)
402 }
403}
404
405impl Neg for CSSPixelLength {
406 type Output = Self;
407
408 #[inline]
409 fn neg(self) -> Self {
410 CSSPixelLength::new(-self.0)
411 }
412}
413
414impl Sub for CSSPixelLength {
415 type Output = Self;
416
417 #[inline]
418 fn sub(self, other: Self) -> Self {
419 Self::new(self.px() - other.px())
420 }
421}
422
423impl SubAssign for CSSPixelLength {
424 #[inline]
425 fn sub_assign(&mut self, other: Self) {
426 self.0 -= other.0;
427 }
428}
429
430impl From<CSSPixelLength> for Au {
431 #[inline]
432 fn from(len: CSSPixelLength) -> Self {
433 Au::from_f32_px(len.0)
434 }
435}
436
437impl From<Au> for CSSPixelLength {
438 #[inline]
439 fn from(len: Au) -> Self {
440 CSSPixelLength::new(len.to_f32_px())
441 }
442}
443
444impl From<CSSPixelLength> for euclid::Length<CSSFloat, CSSPixel> {
445 #[inline]
446 fn from(length: CSSPixelLength) -> Self {
447 Self::new(length.0)
448 }
449}
450
451pub type Length = CSSPixelLength;
453
454pub type LengthOrAuto = generics::GenericLengthPercentageOrAuto<Length>;
456
457pub type NonNegativeLengthOrAuto = generics::GenericLengthPercentageOrAuto<NonNegativeLength>;
459
460pub type LengthOrNumber = GenericLengthOrNumber<Length, Number>;
462
463pub type NonNegativeLength = NonNegative<Length>;
465
466impl ClampToNonNegative for Length {
467 fn clamp_to_non_negative(self) -> Self {
468 Self::new(self.px().max(0.))
469 }
470}
471
472impl NonNegativeLength {
473 #[inline]
475 pub fn new(px: CSSFloat) -> Self {
476 NonNegative(Length::new(px.max(0.)))
477 }
478
479 #[inline]
481 pub fn px(&self) -> CSSFloat {
482 self.0.px()
483 }
484
485 #[inline]
486 pub fn clamp(self) -> Self {
488 if (self.0).0 < 0. {
489 Self::zero()
490 } else {
491 self
492 }
493 }
494}
495
496impl From<Length> for NonNegativeLength {
497 #[inline]
498 fn from(len: Length) -> Self {
499 NonNegative(len)
500 }
501}
502
503impl From<Au> for NonNegativeLength {
504 #[inline]
505 fn from(au: Au) -> Self {
506 NonNegative(au.into())
507 }
508}
509
510impl From<NonNegativeLength> for Au {
511 #[inline]
512 fn from(non_negative_len: NonNegativeLength) -> Self {
513 Au::from(non_negative_len.0)
514 }
515}
516
517pub type NonNegativeLengthPercentageOrNormal =
519 GenericLengthPercentageOrNormal<NonNegativeLengthPercentage>;
520
521pub type NonNegativeLengthOrNumber = GenericLengthOrNumber<NonNegativeLength, NonNegativeNumber>;
523
524pub type Size = GenericSize<NonNegativeLengthPercentage>;
526
527pub type MaxSize = GenericMaxSize<NonNegativeLengthPercentage>;
529
530#[cfg(feature = "gecko")]
531use crate::{
532 gecko_bindings::structs::AnchorPosResolutionParams, logical_geometry::PhysicalAxis,
533 values::generics::length::AnchorSizeKeyword,
534};
535
536#[cfg(feature = "gecko")]
540pub fn resolve_anchor_size(
541 anchor_name: &TreeScoped<DashedIdent>,
542 prop_axis: PhysicalAxis,
543 anchor_size_keyword: AnchorSizeKeyword,
544 params: &AnchorPosResolutionParams,
545) -> Result<Length, ()> {
546 use crate::gecko_bindings::structs::Gecko_GetAnchorPosSize;
547
548 let mut offset = Length::zero();
549 let valid = unsafe {
550 Gecko_GetAnchorPosSize(
551 params,
552 anchor_name.value.0.as_ptr(),
553 &anchor_name.scope,
554 prop_axis as u8,
555 anchor_size_keyword as u8,
556 &mut offset,
557 )
558 };
559
560 if !valid {
561 return Err(());
562 }
563
564 Ok(offset)
565}
566
567pub type Margin = generics::GenericMargin<LengthPercentage>;
569
570impl TryTacticAdjustment for MaxSize {
571 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
572 debug_assert!(
573 old_side.orthogonal_to(new_side),
574 "Sizes should only change axes"
575 );
576 match self {
577 Self::FitContentFunction(lp)
578 | Self::LengthPercentage(lp)
579 | Self::AnchorContainingCalcFunction(lp) => {
580 lp.try_tactic_adjustment(old_side, new_side);
581 },
582 Self::AnchorSizeFunction(s) => s.try_tactic_adjustment(old_side, new_side),
583 Self::None
584 | Self::MaxContent
585 | Self::MinContent
586 | Self::FitContent
587 | Self::WebkitFillAvailable
588 | Self::Stretch => {},
589 #[cfg(feature = "gecko")]
590 Self::MozAvailable => {},
591 }
592 }
593}
594
595impl TryTacticAdjustment for Size {
596 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
597 debug_assert!(
598 old_side.orthogonal_to(new_side),
599 "Sizes should only change axes"
600 );
601 match self {
602 Self::FitContentFunction(lp)
603 | Self::LengthPercentage(lp)
604 | Self::AnchorContainingCalcFunction(lp) => {
605 lp.try_tactic_adjustment(old_side, new_side);
606 },
607 Self::AnchorSizeFunction(s) => s.try_tactic_adjustment(old_side, new_side),
608 Self::Auto
609 | Self::MaxContent
610 | Self::MinContent
611 | Self::FitContent
612 | Self::WebkitFillAvailable
613 | Self::Stretch => {},
614 #[cfg(feature = "gecko")]
615 Self::MozAvailable => {},
616 }
617 }
618}
619
620impl TryTacticAdjustment for Percentage {
621 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
622 if old_side.parallel_to(new_side) {
623 self.0 = 1.0 - self.0;
624 }
625 }
626}
627
628impl TryTacticAdjustment for Margin {
629 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
630 match self {
631 Self::Auto => {},
632 Self::LengthPercentage(lp) | Self::AnchorContainingCalcFunction(lp) => {
633 lp.try_tactic_adjustment(old_side, new_side)
634 },
635 Self::AnchorSizeFunction(anchor) => anchor.try_tactic_adjustment(old_side, new_side),
636 }
637 }
638}