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