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