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