1use super::{Context, Number, ToComputedValue};
8use crate::logical_geometry::PhysicalSide;
9use crate::values::animated::{Context as AnimatedContext, ToAnimatedValue};
10use crate::values::computed::position::TryTacticAdjustment;
11use crate::values::computed::{NonNegativeNumber, Percentage, Zoom};
12use crate::values::generics::length as generics;
13use crate::values::generics::length::{
14 GenericLengthOrNumber, GenericLengthPercentageOrNormal, GenericMaxSize, GenericSize,
15};
16use crate::values::generics::NonNegative;
17use crate::values::resolved::{Context as ResolvedContext, ToResolvedValue};
18use crate::values::specified::length::{AbsoluteLength, FontBaseSize, LineHeightBase};
19use crate::values::{specified, CSSFloat};
20use crate::Zero;
21use app_units::Au;
22use std::fmt::{self, Write};
23use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub, SubAssign};
24use style_traits::{CSSPixel, CssWriter, ToCss};
25
26pub use super::image::Image;
27pub use super::length_percentage::{LengthPercentage, NonNegativeLengthPercentage};
28pub use crate::values::specified::url::UrlOrNone;
29pub use crate::values::specified::{Angle, BorderStyle, Time};
30
31impl ToComputedValue for specified::NoCalcLength {
32 type ComputedValue = Length;
33
34 #[inline]
35 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
36 self.to_computed_value_with_base_size(
37 context,
38 FontBaseSize::CurrentStyle,
39 LineHeightBase::CurrentStyle,
40 )
41 }
42
43 #[inline]
44 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
45 Self::Absolute(AbsoluteLength::Px(computed.px()))
46 }
47}
48
49impl specified::NoCalcLength {
50 pub fn to_computed_value_with_base_size(
52 &self,
53 context: &Context,
54 base_size: FontBaseSize,
55 line_height_base: LineHeightBase,
56 ) -> Length {
57 match *self {
58 Self::Absolute(length) => length.to_computed_value(context),
59 Self::FontRelative(length) => {
60 length.to_computed_value(context, base_size, line_height_base)
61 },
62 Self::ViewportPercentage(length) => length.to_computed_value(context),
63 Self::ContainerRelative(length) => length.to_computed_value(context),
64 Self::ServoCharacterWidth(length) => length
65 .to_computed_value(context.style().get_font().clone_font_size().computed_size()),
66 }
67 }
68}
69
70impl ToComputedValue for specified::Length {
71 type ComputedValue = Length;
72
73 #[inline]
74 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
75 match *self {
76 Self::NoCalc(l) => l.to_computed_value(context),
77 Self::Calc(ref calc) => {
78 let result = calc.to_computed_value(context);
79 debug_assert!(
80 result.to_length().is_some(),
81 "{:?} didn't resolve to a length: {:?}",
82 calc,
83 result,
84 );
85 result.to_length().unwrap_or_else(Length::zero)
86 },
87 }
88 }
89
90 #[inline]
91 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
92 Self::NoCalc(specified::NoCalcLength::from_computed_value(computed))
93 }
94}
95
96macro_rules! computed_length_percentage_or_auto {
99 ($inner:ty) => {
100 #[inline]
102 pub fn to_used_value(&self, percentage_basis: Au) -> Option<Au> {
103 match *self {
104 Self::Auto => None,
105 Self::LengthPercentage(ref lp) => Some(lp.to_used_value(percentage_basis)),
106 }
107 }
108 };
109}
110
111pub type LengthPercentageOrAuto = generics::GenericLengthPercentageOrAuto<LengthPercentage>;
113
114impl LengthPercentageOrAuto {
115 pub fn clamp_to_non_negative(self) -> Self {
117 use crate::values::generics::length::LengthPercentageOrAuto::*;
118 match self {
119 LengthPercentage(l) => LengthPercentage(l.clamp_to_non_negative()),
120 Auto => Auto,
121 }
122 }
123
124 pub fn as_ref(&self) -> generics::GenericLengthPercentageOrAuto<&LengthPercentage> {
126 use crate::values::generics::length::LengthPercentageOrAuto::*;
127 match *self {
128 LengthPercentage(ref lp) => LengthPercentage(lp),
129 Auto => Auto,
130 }
131 }
132
133 computed_length_percentage_or_auto!(LengthPercentage);
134}
135
136impl generics::GenericLengthPercentageOrAuto<&LengthPercentage> {
137 #[inline]
139 pub fn percentage_relative_to(&self, basis: Length) -> LengthOrAuto {
140 use crate::values::generics::length::LengthPercentageOrAuto::*;
141 match self {
142 LengthPercentage(length_percentage) => {
143 LengthPercentage(length_percentage.percentage_relative_to(basis))
144 },
145 Auto => Auto,
146 }
147 }
148
149 #[inline]
151 pub fn maybe_percentage_relative_to(&self, basis: Option<Length>) -> LengthOrAuto {
152 use crate::values::generics::length::LengthPercentageOrAuto::*;
153 match self {
154 LengthPercentage(length_percentage) => length_percentage
155 .maybe_percentage_relative_to(basis)
156 .map_or(Auto, LengthPercentage),
157 Auto => Auto,
158 }
159 }
160}
161
162pub type NonNegativeLengthPercentageOrAuto =
164 generics::GenericLengthPercentageOrAuto<NonNegativeLengthPercentage>;
165
166impl NonNegativeLengthPercentageOrAuto {
167 computed_length_percentage_or_auto!(NonNegativeLengthPercentage);
168}
169
170#[derive(
172 Animate,
173 Clone,
174 ComputeSquaredDistance,
175 Copy,
176 Deserialize,
177 MallocSizeOf,
178 PartialEq,
179 PartialOrd,
180 Serialize,
181 ToAnimatedZero,
182 ToComputedValue,
183 ToShmem,
184 ToTyped,
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 std::iter::Sum for CSSPixelLength {
337 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
338 iter.fold(Length::zero(), Add::add)
339 }
340}
341
342impl Add for CSSPixelLength {
343 type Output = Self;
344
345 #[inline]
346 fn add(self, other: Self) -> Self {
347 Self::new(self.px() + other.px())
348 }
349}
350
351impl AddAssign for CSSPixelLength {
352 #[inline]
353 fn add_assign(&mut self, other: Self) {
354 self.0 += other.0;
355 }
356}
357
358impl Div for CSSPixelLength {
359 type Output = CSSFloat;
360
361 #[inline]
362 fn div(self, other: Self) -> CSSFloat {
363 self.px() / other.px()
364 }
365}
366
367impl Div<CSSFloat> for CSSPixelLength {
368 type Output = Self;
369
370 #[inline]
371 fn div(self, other: CSSFloat) -> Self {
372 Self::new(self.px() / other)
373 }
374}
375
376impl MulAssign<CSSFloat> for CSSPixelLength {
377 #[inline]
378 fn mul_assign(&mut self, other: CSSFloat) {
379 self.0 *= other;
380 }
381}
382
383impl Mul<CSSFloat> for CSSPixelLength {
384 type Output = Self;
385
386 #[inline]
387 fn mul(self, other: CSSFloat) -> Self {
388 Self::new(self.px() * other)
389 }
390}
391
392impl Neg for CSSPixelLength {
393 type Output = Self;
394
395 #[inline]
396 fn neg(self) -> Self {
397 CSSPixelLength::new(-self.0)
398 }
399}
400
401impl Sub for CSSPixelLength {
402 type Output = Self;
403
404 #[inline]
405 fn sub(self, other: Self) -> Self {
406 Self::new(self.px() - other.px())
407 }
408}
409
410impl SubAssign for CSSPixelLength {
411 #[inline]
412 fn sub_assign(&mut self, other: Self) {
413 self.0 -= other.0;
414 }
415}
416
417impl From<CSSPixelLength> for Au {
418 #[inline]
419 fn from(len: CSSPixelLength) -> Self {
420 Au::from_f32_px(len.0)
421 }
422}
423
424impl From<Au> for CSSPixelLength {
425 #[inline]
426 fn from(len: Au) -> Self {
427 CSSPixelLength::new(len.to_f32_px())
428 }
429}
430
431impl From<CSSPixelLength> for euclid::Length<CSSFloat, CSSPixel> {
432 #[inline]
433 fn from(length: CSSPixelLength) -> Self {
434 Self::new(length.0)
435 }
436}
437
438pub type Length = CSSPixelLength;
440
441pub type LengthOrAuto = generics::GenericLengthPercentageOrAuto<Length>;
443
444pub type NonNegativeLengthOrAuto = generics::GenericLengthPercentageOrAuto<NonNegativeLength>;
446
447pub type LengthOrNumber = GenericLengthOrNumber<Length, Number>;
449
450pub type NonNegativeLength = NonNegative<Length>;
452
453impl ToAnimatedValue for NonNegativeLength {
454 type AnimatedValue = Length;
455
456 #[inline]
457 fn to_animated_value(self, context: &AnimatedContext) -> Self::AnimatedValue {
458 self.0.to_animated_value(context)
459 }
460
461 #[inline]
462 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
463 NonNegativeLength::new(animated.px().max(0.))
464 }
465}
466
467impl NonNegativeLength {
468 #[inline]
470 pub fn new(px: CSSFloat) -> Self {
471 NonNegative(Length::new(px.max(0.)))
472 }
473
474 #[inline]
476 pub fn px(&self) -> CSSFloat {
477 self.0.px()
478 }
479
480 #[inline]
481 pub fn clamp(self) -> Self {
483 if (self.0).0 < 0. {
484 Self::zero()
485 } else {
486 self
487 }
488 }
489}
490
491impl From<Length> for NonNegativeLength {
492 #[inline]
493 fn from(len: Length) -> Self {
494 NonNegative(len)
495 }
496}
497
498impl From<Au> for NonNegativeLength {
499 #[inline]
500 fn from(au: Au) -> Self {
501 NonNegative(au.into())
502 }
503}
504
505impl From<NonNegativeLength> for Au {
506 #[inline]
507 fn from(non_negative_len: NonNegativeLength) -> Self {
508 Au::from(non_negative_len.0)
509 }
510}
511
512pub type NonNegativeLengthPercentageOrNormal =
514 GenericLengthPercentageOrNormal<NonNegativeLengthPercentage>;
515
516pub type NonNegativeLengthOrNumber = GenericLengthOrNumber<NonNegativeLength, NonNegativeNumber>;
518
519pub type Size = GenericSize<NonNegativeLengthPercentage>;
521
522pub type MaxSize = GenericMaxSize<NonNegativeLengthPercentage>;
524
525#[cfg(feature = "gecko")]
526use crate::{
527 gecko_bindings::structs::AnchorPosResolutionParams, logical_geometry::PhysicalAxis,
528 values::generics::length::AnchorSizeKeyword, values::DashedIdent,
529};
530
531#[cfg(feature = "gecko")]
535pub fn resolve_anchor_size(
536 anchor_name: &DashedIdent,
537 prop_axis: PhysicalAxis,
538 anchor_size_keyword: AnchorSizeKeyword,
539 params: &AnchorPosResolutionParams,
540) -> Result<Length, ()> {
541 use crate::gecko_bindings::structs::Gecko_GetAnchorPosSize;
542
543 let mut offset = Length::zero();
544 let valid = unsafe {
545 Gecko_GetAnchorPosSize(
546 params,
547 anchor_name.0.as_ptr(),
548 prop_axis as u8,
549 anchor_size_keyword as u8,
550 &mut offset,
551 )
552 };
553
554 if !valid {
555 return Err(());
556 }
557
558 Ok(offset)
559}
560
561pub type Margin = generics::GenericMargin<LengthPercentage>;
563
564impl TryTacticAdjustment for MaxSize {
565 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
566 debug_assert!(
567 old_side.orthogonal_to(new_side),
568 "Sizes should only change axes"
569 );
570 match self {
571 Self::FitContentFunction(lp)
572 | Self::LengthPercentage(lp)
573 | Self::AnchorContainingCalcFunction(lp) => {
574 lp.try_tactic_adjustment(old_side, new_side);
575 },
576 Self::AnchorSizeFunction(s) => s.try_tactic_adjustment(old_side, new_side),
577 Self::None
578 | Self::MaxContent
579 | Self::MinContent
580 | Self::FitContent
581 | Self::WebkitFillAvailable
582 | Self::Stretch => {},
583 #[cfg(feature = "gecko")]
584 Self::MozAvailable => {},
585 }
586 }
587}
588
589impl TryTacticAdjustment for Size {
590 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
591 debug_assert!(
592 old_side.orthogonal_to(new_side),
593 "Sizes should only change axes"
594 );
595 match self {
596 Self::FitContentFunction(lp)
597 | Self::LengthPercentage(lp)
598 | Self::AnchorContainingCalcFunction(lp) => {
599 lp.try_tactic_adjustment(old_side, new_side);
600 },
601 Self::AnchorSizeFunction(s) => s.try_tactic_adjustment(old_side, new_side),
602 Self::Auto
603 | Self::MaxContent
604 | Self::MinContent
605 | Self::FitContent
606 | Self::WebkitFillAvailable
607 | Self::Stretch => {},
608 #[cfg(feature = "gecko")]
609 Self::MozAvailable => {},
610 }
611 }
612}
613
614impl TryTacticAdjustment for Percentage {
615 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
616 if old_side.parallel_to(new_side) {
617 self.0 = 1.0 - self.0;
618 }
619 }
620}
621
622impl TryTacticAdjustment for Margin {
623 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
624 match self {
625 Self::Auto => {},
626 Self::LengthPercentage(lp) | Self::AnchorContainingCalcFunction(lp) => {
627 lp.try_tactic_adjustment(old_side, new_side)
628 },
629 Self::AnchorSizeFunction(anchor) => anchor.try_tactic_adjustment(old_side, new_side),
630 }
631 }
632}