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