1use super::{position::AnchorSide, Context, Length, Percentage, ToComputedValue};
12use crate::derives::*;
13#[cfg(feature = "gecko")]
14use crate::gecko_bindings::structs::{AnchorPosOffsetResolutionParams, GeckoFontMetrics};
15use crate::logical_geometry::{PhysicalAxis, PhysicalSide};
16use crate::typed_om::{ToTyped, TypedValue};
17use crate::values::animated::{
18 Animate, Context as AnimatedContext, Procedure, ToAnimatedValue, ToAnimatedZero,
19};
20use crate::values::computed::position::TryTacticAdjustment;
21use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
22use crate::values::generics::calc::GenericAnchorFunctionFallback;
23#[cfg(feature = "gecko")]
24use crate::values::generics::length::AnchorResolutionResult;
25use crate::values::generics::position::GenericAnchorSide;
26use crate::values::generics::{calc, ClampToNonNegative, NonNegative};
27use crate::values::resolved::{Context as ResolvedContext, ToResolvedValue};
28use crate::values::specified::length::{EqualsPercentage, FontBaseSize, LineHeightBase};
29use crate::values::specified::number::NoCalcNumber;
30use crate::values::specified::percentage::NoCalcPercentage;
31use crate::values::tagged_numeric::{self as tagged, NumericUnion};
32use crate::values::{specified, CSSFloat};
33use crate::{Zero, ZeroNoPercent};
34use app_units::Au;
35use serde::{Deserialize, Serialize};
36use std::fmt::{self, Write};
37use style_traits::values::specified::AllowedNumericType;
38use style_traits::{CssWriter, ToCss};
39use thin_vec::ThinVec;
40
41pub use super::calc::ComputedLeaf;
42
43#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToShmem)]
45#[repr(u8)]
46pub enum LengthPercentageTag {
47 Length = 0,
49 Percentage = 1,
51}
52
53#[derive(MallocSizeOf)]
61#[repr(C)]
62pub struct LengthPercentage(NumericUnion<LengthPercentageTag, f32, CalcLengthPercentage>);
63
64impl ToAnimatedValue for LengthPercentage {
65 type AnimatedValue = Self;
66
67 fn to_animated_value(self, context: &AnimatedContext) -> Self::AnimatedValue {
68 if context.style.effective_zoom.is_one() {
69 return self;
70 }
71 self.map_lengths(|l| l.to_animated_value(context))
72 }
73
74 #[inline]
75 fn from_animated_value(value: Self::AnimatedValue) -> Self {
76 value
77 }
78}
79
80impl ToResolvedValue for LengthPercentage {
81 type ResolvedValue = Self;
82
83 fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {
84 if context.style.effective_zoom.is_one() {
85 return self;
86 }
87 self.map_lengths(|l| l.to_resolved_value(context))
88 }
89
90 #[inline]
91 fn from_resolved_value(value: Self::ResolvedValue) -> Self {
92 value
93 }
94}
95
96impl EqualsPercentage for LengthPercentage {
97 fn equals_percentage(&self, v: CSSFloat) -> bool {
98 match self.unpack() {
99 Unpacked::Percentage(p) => p.0 == v,
100 _ => false,
101 }
102 }
103}
104
105#[derive(Clone, Debug, PartialEq, ToCss, ToTyped)]
107pub enum Unpacked<'a> {
108 Calc(&'a CalcLengthPercentage),
110 Length(Length),
112 Percentage(Percentage),
114}
115
116enum UnpackedMut<'a> {
118 Calc(&'a mut CalcLengthPercentage),
119 Length(Length),
120 Percentage(Percentage),
121}
122
123#[derive(Deserialize, PartialEq, Serialize)]
126enum Serializable {
127 Calc(CalcLengthPercentage),
128 Length(Length),
129 Percentage(Percentage),
130}
131
132impl LengthPercentage {
133 #[inline]
135 pub fn one() -> Self {
136 Self::new_length(Length::new(1.))
137 }
138
139 #[inline]
141 pub fn zero_percent() -> Self {
142 Self::new_percent(Percentage::zero())
143 }
144
145 #[inline]
147 pub fn hundred_percent() -> Self {
148 Self::new_percent(Percentage::hundred())
149 }
150
151 fn to_calc_node(&self) -> CalcNode {
152 match self.unpack() {
153 Unpacked::Length(l) => CalcNode::Leaf(ComputedLeaf::Length(l)),
154 Unpacked::Percentage(p) => CalcNode::Leaf(ComputedLeaf::Percentage(p)),
155 Unpacked::Calc(p) => p.node.clone(),
156 }
157 }
158
159 fn map_lengths(&self, mut map_fn: impl FnMut(Length) -> Length) -> Self {
160 match self.unpack() {
161 Unpacked::Length(l) => Self::new_length(map_fn(l)),
162 Unpacked::Percentage(p) => Self::new_percent(p),
163 Unpacked::Calc(lp) => Self::new_calc_unchecked(Box::new(CalcLengthPercentage {
164 clamping_mode: lp.clamping_mode,
165 node: lp.node.map_leaves(|leaf| match *leaf {
166 ComputedLeaf::Length(ref l) => ComputedLeaf::Length(map_fn(*l)),
167 ref l => l.clone(),
168 }),
169 })),
170 }
171 }
172
173 #[inline]
175 pub fn new_length(length: Length) -> Self {
176 Self(NumericUnion::inline(
177 LengthPercentageTag::Length,
178 length.px(),
179 ))
180 }
181
182 #[inline]
184 pub fn new_percent(percentage: Percentage) -> Self {
185 Self(NumericUnion::inline(
186 LengthPercentageTag::Percentage,
187 percentage.0,
188 ))
189 }
190
191 pub fn hundred_percent_minus(v: Self, clamping_mode: AllowedNumericType) -> Self {
194 let mut node = v.to_calc_node();
197 node.negate();
198
199 let new_node = CalcNode::Sum(
200 vec![
201 CalcNode::Leaf(ComputedLeaf::Percentage(Percentage::hundred())),
202 node,
203 ]
204 .into(),
205 );
206
207 Self::new_calc(new_node, clamping_mode)
208 }
209
210 pub fn hundred_percent_minus_list(list: &[&Self], clamping_mode: AllowedNumericType) -> Self {
213 let mut new_list = vec![CalcNode::Leaf(ComputedLeaf::Percentage(
214 Percentage::hundred(),
215 ))];
216
217 for lp in list.iter() {
218 let mut node = lp.to_calc_node();
219 node.negate();
220 new_list.push(node)
221 }
222
223 Self::new_calc(CalcNode::Sum(new_list.into()), clamping_mode)
224 }
225
226 #[inline]
228 pub fn new_calc(mut node: CalcNode, clamping_mode: AllowedNumericType) -> Self {
229 node.simplify_and_sort();
230
231 match node {
232 CalcNode::Leaf(l) => {
233 return match l {
234 ComputedLeaf::Length(l) => {
235 Self::new_length(Length::new(clamping_mode.clamp(l.px())).normalized())
236 },
237 ComputedLeaf::Percentage(p) => Self::new_percent(Percentage(
238 clamping_mode.clamp(crate::values::normalize(p.0)),
239 )),
240 ComputedLeaf::Number(number) => {
241 debug_assert!(
242 false,
243 "The final result of a <length-percentage> should never be a number"
244 );
245 Self::new_length(Length::new(number))
246 },
247 ComputedLeaf::Angle(..)
248 | ComputedLeaf::Time(..)
249 | ComputedLeaf::Resolution(..) => {
250 debug_assert!(
251 false,
252 "The final result of a <length-percentage> should never be an angle, time, or resolution"
253 );
254 Self::zero()
255 },
256 };
257 },
258 _ => Self::new_calc_unchecked(Box::new(CalcLengthPercentage {
259 clamping_mode,
260 node,
261 })),
262 }
263 }
264
265 fn new_calc_unchecked(calc: Box<CalcLengthPercentage>) -> Self {
268 Self(NumericUnion::boxed(calc))
269 }
270
271 #[inline]
272 fn unpack_mut<'a>(&'a mut self) -> UnpackedMut<'a> {
273 match self.0.unpack_mut() {
274 tagged::UnpackedMut::Boxed(calc) => UnpackedMut::Calc(calc),
275 tagged::UnpackedMut::Inline(t, n) => match *t {
276 LengthPercentageTag::Length => UnpackedMut::Length(Length::new(*n)),
277 LengthPercentageTag::Percentage => UnpackedMut::Percentage(Percentage(*n)),
278 },
279 }
280 }
281
282 #[inline]
285 pub fn unpack<'a>(&'a self) -> Unpacked<'a> {
286 match self.0.unpack() {
287 tagged::Unpacked::Boxed(calc) => Unpacked::Calc(calc),
288 tagged::Unpacked::Inline(LengthPercentageTag::Length, v) => {
289 Unpacked::Length(Length::new(v))
290 },
291 tagged::Unpacked::Inline(LengthPercentageTag::Percentage, v) => {
292 Unpacked::Percentage(Percentage(v))
293 },
294 }
295 }
296
297 #[inline]
298 fn to_serializable(&self) -> Serializable {
299 match self.unpack() {
300 Unpacked::Calc(c) => Serializable::Calc(c.clone()),
301 Unpacked::Length(l) => Serializable::Length(l),
302 Unpacked::Percentage(p) => Serializable::Percentage(p),
303 }
304 }
305
306 #[inline]
307 fn from_serializable(s: Serializable) -> Self {
308 match s {
309 Serializable::Calc(c) => Self::new_calc_unchecked(Box::new(c)),
310 Serializable::Length(l) => Self::new_length(l),
311 Serializable::Percentage(p) => Self::new_percent(p),
312 }
313 }
314
315 #[inline]
317 pub fn resolve(&self, basis: Length) -> Length {
318 match self.unpack() {
319 Unpacked::Length(l) => l,
320 Unpacked::Percentage(p) => (basis * p.0).normalized(),
321 Unpacked::Calc(ref c) => c.resolve(basis),
322 }
323 }
324
325 #[inline]
327 pub fn percentage_relative_to(&self, basis: Length) -> Length {
328 self.resolve(basis)
329 }
330
331 #[inline]
333 pub fn has_percentage(&self) -> bool {
334 match self.unpack() {
335 Unpacked::Length(..) => false,
336 Unpacked::Percentage(..) | Unpacked::Calc(..) => true,
337 }
338 }
339
340 pub fn to_length(&self) -> Option<Length> {
342 match self.unpack() {
343 Unpacked::Length(l) => Some(l),
344 Unpacked::Percentage(..) | Unpacked::Calc(..) => {
345 debug_assert!(self.has_percentage());
346 return None;
347 },
348 }
349 }
350
351 #[inline]
353 pub fn to_percentage(&self) -> Option<Percentage> {
354 match self.unpack() {
355 Unpacked::Percentage(p) => Some(p),
356 Unpacked::Length(..) | Unpacked::Calc(..) => None,
357 }
358 }
359
360 #[inline]
362 pub fn to_percentage_of(&self, basis: Length) -> Option<Percentage> {
363 if basis.px() == 0. {
364 return None;
365 }
366 Some(match self.unpack() {
367 Unpacked::Length(l) => Percentage(l.px() / basis.px()),
368 Unpacked::Percentage(p) => p,
369 Unpacked::Calc(ref c) => Percentage(c.resolve(basis).px() / basis.px()),
370 })
371 }
372
373 #[inline]
375 pub fn to_used_value(&self, containing_length: Au) -> Au {
376 let length = self.to_pixel_length(containing_length);
377 if let Unpacked::Percentage(_) = self.unpack() {
378 return Au::from_f32_px_trunc(length.px());
379 }
380 Au::from(length)
381 }
382
383 #[inline]
385 pub fn to_pixel_length(&self, containing_length: Au) -> Length {
386 self.resolve(containing_length.into())
387 }
388
389 #[inline]
391 pub fn maybe_to_used_value(&self, container_len: Option<Au>) -> Option<Au> {
392 self.maybe_percentage_relative_to(container_len.map(Length::from))
393 .map(if let Unpacked::Percentage(_) = self.unpack() {
394 |length: Length| Au::from_f32_px_trunc(length.px())
395 } else {
396 Au::from
397 })
398 }
399
400 pub fn maybe_percentage_relative_to(&self, container_len: Option<Length>) -> Option<Length> {
404 if let Unpacked::Length(l) = self.unpack() {
405 return Some(l);
406 }
407 Some(self.resolve(container_len?))
408 }
409}
410
411impl ClampToNonNegative for LengthPercentage {
412 #[inline]
414 fn clamp_to_non_negative(mut self) -> Self {
415 match self.unpack_mut() {
416 UnpackedMut::Length(l) => Self::new_length(l.clamp_to_non_negative()),
417 UnpackedMut::Percentage(p) => Self::new_percent(p.clamp_to_non_negative()),
418 UnpackedMut::Calc(ref mut c) => {
419 c.clamping_mode = AllowedNumericType::NonNegative;
420 self
421 },
422 }
423 }
424}
425
426impl PartialEq for LengthPercentage {
427 fn eq(&self, other: &Self) -> bool {
428 self.unpack() == other.unpack()
429 }
430}
431
432impl fmt::Debug for LengthPercentage {
433 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
434 self.unpack().fmt(formatter)
435 }
436}
437
438impl ToAnimatedZero for LengthPercentage {
439 fn to_animated_zero(&self) -> Result<Self, ()> {
440 Ok(match self.unpack() {
441 Unpacked::Length(l) => Self::new_length(l.to_animated_zero()?),
442 Unpacked::Percentage(p) => Self::new_percent(p.to_animated_zero()?),
443 Unpacked::Calc(c) => Self::new_calc_unchecked(Box::new(c.to_animated_zero()?)),
444 })
445 }
446}
447
448impl Clone for LengthPercentage {
449 fn clone(&self) -> Self {
450 match self.unpack() {
451 Unpacked::Length(l) => Self::new_length(l),
452 Unpacked::Percentage(p) => Self::new_percent(p),
453 Unpacked::Calc(c) => Self::new_calc_unchecked(Box::new(c.clone())),
454 }
455 }
456}
457
458impl ToComputedValue for specified::LengthPercentage {
459 type ComputedValue = LengthPercentage;
460
461 fn to_computed_value(&self, context: &Context) -> LengthPercentage {
462 match *self {
463 specified::LengthPercentage::Length(ref value) => {
464 LengthPercentage::new_length(value.to_computed_value(context))
465 },
466 specified::LengthPercentage::Percentage(value) => {
467 LengthPercentage::new_percent(value.to_computed_value(context))
468 },
469 specified::LengthPercentage::Calc(ref calc) => (**calc).to_computed_value(context),
470 }
471 }
472
473 fn from_computed_value(computed: &LengthPercentage) -> Self {
474 match computed.unpack() {
475 Unpacked::Length(ref l) => {
476 specified::LengthPercentage::Length(ToComputedValue::from_computed_value(l))
477 },
478 Unpacked::Percentage(p) => {
479 specified::LengthPercentage::Percentage(NoCalcPercentage::new(p.0))
480 },
481 Unpacked::Calc(c) => {
482 specified::LengthPercentage::Calc(Box::new(
485 specified::CalcLengthPercentage::from_computed_value(c),
486 ))
487 },
488 }
489 }
490}
491
492impl ComputeSquaredDistance for LengthPercentage {
493 #[inline]
494 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
495 let basis = Length::new(100.);
500 self.resolve(basis)
501 .compute_squared_distance(&other.resolve(basis))
502 }
503}
504
505impl ToCss for LengthPercentage {
506 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
507 where
508 W: Write,
509 {
510 self.unpack().to_css(dest)
511 }
512}
513
514impl ToTyped for LengthPercentage {
515 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
516 self.unpack().to_typed(dest)
517 }
518}
519
520impl Zero for LengthPercentage {
521 fn zero() -> Self {
522 LengthPercentage::new_length(Length::zero())
523 }
524
525 #[inline]
527 fn is_zero(&self) -> bool {
528 match self.unpack() {
529 Unpacked::Length(l) => l.px() == 0.0,
530 Unpacked::Percentage(p) => p.0 == 0.0,
531 Unpacked::Calc(..) => false,
532 }
533 }
534}
535
536impl ZeroNoPercent for LengthPercentage {
537 #[inline]
538 fn is_zero_no_percent(&self) -> bool {
539 self.to_length().is_some_and(|l| l.px() == 0.0)
540 }
541}
542
543impl Serialize for LengthPercentage {
544 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
545 where
546 S: serde::Serializer,
547 {
548 self.to_serializable().serialize(serializer)
549 }
550}
551
552impl<'de> Deserialize<'de> for LengthPercentage {
553 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
554 where
555 D: serde::Deserializer<'de>,
556 {
557 Ok(Self::from_serializable(Serializable::deserialize(
558 deserializer,
559 )?))
560 }
561}
562
563pub type CalcNode = calc::GenericCalcNode<ComputedLeaf>;
565
566#[derive(
568 Clone,
569 Debug,
570 Deserialize,
571 MallocSizeOf,
572 Serialize,
573 ToAnimatedZero,
574 ToResolvedValue,
575 ToCss,
576 ToTyped,
577)]
578#[repr(C)]
579pub struct CalcLengthPercentage {
580 #[animation(constant)]
581 #[css(skip)]
582 clamping_mode: AllowedNumericType,
583 node: CalcNode,
584}
585
586pub type CalcAnchorSide = GenericAnchorSide<Box<CalcNode>>;
588
589pub struct CalcLengthPercentageResolution {
591 pub result: Length,
593 pub percentage_used: bool,
595}
596
597#[repr(C)]
600#[derive(Clone, Copy)]
601pub enum AllowAnchorPosResolutionInCalcPercentage {
602 Both(PhysicalSide),
604 AnchorSizeOnly(PhysicalAxis),
606}
607
608impl AllowAnchorPosResolutionInCalcPercentage {
609 #[cfg(feature = "gecko")]
610 pub fn to_axis(&self) -> PhysicalAxis {
612 match self {
613 Self::AnchorSizeOnly(axis) => *axis,
614 Self::Both(side) => {
615 if matches!(side, PhysicalSide::Top | PhysicalSide::Bottom) {
616 PhysicalAxis::Vertical
617 } else {
618 PhysicalAxis::Horizontal
619 }
620 },
621 }
622 }
623}
624
625impl From<&CalcAnchorSide> for AnchorSide {
626 fn from(value: &CalcAnchorSide) -> Self {
627 match value {
628 CalcAnchorSide::Keyword(k) => Self::Keyword(*k),
629 CalcAnchorSide::Percentage(p) => {
630 if let CalcNode::Leaf(ComputedLeaf::Percentage(p)) = **p {
631 Self::Percentage(p)
632 } else {
633 unreachable!("Should have parsed simplified percentage.");
634 }
635 },
636 }
637 }
638}
639
640impl CalcLengthPercentage {
641 #[inline]
643 pub fn resolve(&self, basis: Length) -> Length {
644 if let ComputedLeaf::Length(px) = self
646 .node
647 .resolve_map(|leaf| {
648 Ok(if let ComputedLeaf::Percentage(p) = leaf {
649 ComputedLeaf::Length(Length::new(basis.px() * p.0))
650 } else {
651 leaf.clone()
652 })
653 })
654 .unwrap()
655 {
656 Length::new(self.clamping_mode.clamp(px.px())).normalized()
657 } else {
658 unreachable!("resolve_map should turn percentages to lengths, and parsing should ensure that we don't end up with a number");
659 }
660 }
661
662 #[inline]
665 #[cfg(feature = "gecko")]
666 pub fn resolve_anchor(
667 &self,
668 allowed: AllowAnchorPosResolutionInCalcPercentage,
669 params: &AnchorPosOffsetResolutionParams,
670 ) -> Result<(CalcNode, AllowedNumericType), ()> {
671 use crate::values::{
672 computed::{length::resolve_anchor_size, AnchorFunction},
673 generics::{length::GenericAnchorSizeFunction, position::GenericAnchorFunction},
674 };
675
676 fn resolve_anchor_function<'a>(
677 f: &'a GenericAnchorFunction<
678 Box<CalcNode>,
679 Box<GenericAnchorFunctionFallback<ComputedLeaf>>,
680 >,
681 side: PhysicalSide,
682 params: &AnchorPosOffsetResolutionParams,
683 ) -> AnchorResolutionResult<'a, Box<CalcNode>> {
684 let anchor_side: &CalcAnchorSide = &f.side;
685 let resolved = if f.valid_for(side, params.mBaseParams.mPosition) {
686 AnchorFunction::resolve(&f.target_element, &anchor_side.into(), side, params).ok()
687 } else {
688 None
689 };
690
691 resolved.map_or_else(
692 || {
693 let Some(fb) = f.fallback.as_ref() else {
694 return AnchorResolutionResult::Invalid;
695 };
696 let mut node = Box::new(fb.node.clone());
697 let result = node.map_node(|node| {
698 resolve_anchor_functions(
699 node,
700 AllowAnchorPosResolutionInCalcPercentage::Both(side),
701 params,
702 )
703 });
704 if result.is_err() {
705 return AnchorResolutionResult::Invalid;
706 }
707 AnchorResolutionResult::Resolved(node)
708 },
709 |v| {
710 AnchorResolutionResult::Resolved(Box::new(CalcNode::Leaf(
711 ComputedLeaf::Length(v),
712 )))
713 },
714 )
715 }
716
717 fn resolve_anchor_size_function<'a>(
718 f: &'a GenericAnchorSizeFunction<Box<GenericAnchorFunctionFallback<ComputedLeaf>>>,
719 allowed: AllowAnchorPosResolutionInCalcPercentage,
720 params: &AnchorPosOffsetResolutionParams,
721 ) -> AnchorResolutionResult<'a, Box<CalcNode>> {
722 let axis = allowed.to_axis();
723 let resolved = if f.valid_for(params.mBaseParams.mPosition) {
724 resolve_anchor_size(&f.target_element, axis, f.size, ¶ms.mBaseParams).ok()
725 } else {
726 None
727 };
728
729 resolved.map_or_else(
730 || {
731 let Some(fb) = f.fallback.as_ref() else {
732 return AnchorResolutionResult::Invalid;
733 };
734 let mut node = Box::new(fb.node.clone());
735 let result =
736 node.map_node(|node| resolve_anchor_functions(node, allowed, params));
737 if result.is_err() {
738 return AnchorResolutionResult::Invalid;
739 }
740 AnchorResolutionResult::Resolved(node)
741 },
742 |v| {
743 AnchorResolutionResult::Resolved(Box::new(CalcNode::Leaf(
744 ComputedLeaf::Length(v),
745 )))
746 },
747 )
748 }
749
750 fn resolve_anchor_functions(
751 node: &CalcNode,
752 allowed: AllowAnchorPosResolutionInCalcPercentage,
753 params: &AnchorPosOffsetResolutionParams,
754 ) -> Result<Option<CalcNode>, ()> {
755 let resolution = match node {
756 CalcNode::Anchor(f) => {
757 let prop_side = match allowed {
758 AllowAnchorPosResolutionInCalcPercentage::Both(side) => side,
759 AllowAnchorPosResolutionInCalcPercentage::AnchorSizeOnly(_) => {
760 unreachable!("anchor() found where disallowed")
761 },
762 };
763 resolve_anchor_function(f, prop_side, params)
764 },
765 CalcNode::AnchorSize(f) => resolve_anchor_size_function(f, allowed, params),
766 _ => return Ok(None),
767 };
768
769 match resolution {
770 AnchorResolutionResult::Invalid => Err(()),
771 AnchorResolutionResult::Fallback(fb) => {
772 Ok(Some(*fb.clone()))
774 },
775 AnchorResolutionResult::Resolved(v) => Ok(Some(*v.clone())),
776 }
777 }
778
779 let mut node = self.node.clone();
780 node.map_node(|node| resolve_anchor_functions(node, allowed, params))?;
781 Ok((node, self.clamping_mode))
782 }
783}
784
785impl PartialEq for CalcLengthPercentage {
798 fn eq(&self, other: &Self) -> bool {
799 self.node == other.node
800 }
801}
802
803impl specified::CalcLengthPercentage {
804 fn to_computed_value_with_zoom<F>(
806 &self,
807 context: &Context,
808 zoom_fn: F,
809 base_size: FontBaseSize,
810 line_height_base: LineHeightBase,
811 ) -> LengthPercentage
812 where
813 F: Fn(Length) -> Length,
814 {
815 use crate::values::specified::calc::Leaf;
816
817 let node = self.0.node.map_leaves(|leaf| match *leaf {
818 Leaf::Percentage(p) => ComputedLeaf::Percentage(Percentage(p.get())),
819 Leaf::Length(l) => ComputedLeaf::Length({
820 let result =
821 l.to_computed_value_with_base_size(context, base_size, line_height_base);
822 if l.should_zoom_text() {
823 zoom_fn(result)
824 } else {
825 result
826 }
827 }),
828 Leaf::Number(n) => ComputedLeaf::Number(n.get()),
829 Leaf::Angle(a) => {
830 ComputedLeaf::Angle(specified::Angle::new(a).to_computed_value(context))
831 },
832 Leaf::Time(t) => ComputedLeaf::Time(specified::Time::new(t).to_computed_value(context)),
833 Leaf::Resolution(r) => {
834 ComputedLeaf::Resolution(specified::Resolution::new(r).to_computed_value(context))
835 },
836 Leaf::ColorComponent(..) => unreachable!("Shouldn't have parsed"),
837 });
838
839 LengthPercentage::new_calc(node, self.0.clamping_mode)
840 }
841
842 pub fn to_computed_value_zoomed(
844 &self,
845 context: &Context,
846 base_size: FontBaseSize,
847 line_height_base: LineHeightBase,
848 ) -> LengthPercentage {
849 self.to_computed_value_with_zoom(
850 context,
851 |abs| context.maybe_zoom_text(abs),
852 base_size,
853 line_height_base,
854 )
855 }
856
857 pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
860 use crate::values::specified::calc::Leaf;
861
862 match self.0.node {
865 calc::CalcNode::Leaf(Leaf::Length(ref l)) => {
866 l.to_computed_pixel_length_without_context()
867 },
868 _ => Err(()),
869 }
870 }
871
872 #[cfg(feature = "gecko")]
875 pub fn to_computed_pixel_length_with_font_metrics(
876 &self,
877 get_font_metrics: Option<impl Fn() -> GeckoFontMetrics>,
878 ) -> Result<CSSFloat, ()> {
879 use crate::values::specified::calc::Leaf;
880
881 match self.0.node {
882 calc::CalcNode::Leaf(Leaf::Length(ref l)) => {
883 l.to_computed_pixel_length_with_font_metrics(get_font_metrics)
884 },
885 _ => Err(()),
886 }
887 }
888
889 pub fn to_computed_value(&self, context: &Context) -> LengthPercentage {
891 self.to_computed_value_with_zoom(
892 context,
893 |abs| abs,
894 FontBaseSize::CurrentStyle,
895 LineHeightBase::CurrentStyle,
896 )
897 }
898
899 #[inline]
900 fn from_computed_value(computed: &CalcLengthPercentage) -> Self {
901 use crate::values::specified::angle::NoCalcAngle;
902 use crate::values::specified::calc::Leaf;
903 use crate::values::specified::length::NoCalcLength;
904 use crate::values::specified::resolution::NoCalcResolution;
905 use crate::values::specified::time::NoCalcTime;
906
907 specified::CalcLengthPercentage(specified::CalcNumeric {
908 clamping_mode: computed.clamping_mode,
909 node: computed.node.map_leaves(|l| match l {
910 ComputedLeaf::Length(ref l) => Leaf::Length(NoCalcLength::from_px(l.px())),
911 ComputedLeaf::Percentage(ref p) => Leaf::Percentage(NoCalcPercentage::new(p.0)),
912 ComputedLeaf::Number(n) => Leaf::Number(NoCalcNumber::new(*n)),
913 ComputedLeaf::Angle(a) => Leaf::Angle(NoCalcAngle::from_degrees(a.degrees())),
914 ComputedLeaf::Time(t) => Leaf::Time(NoCalcTime::from_seconds(t.seconds())),
915 ComputedLeaf::Resolution(r) => {
916 Leaf::Resolution(NoCalcResolution::from_dppx(r.dppx()))
917 },
918 }),
919 })
920 }
921}
922
923impl Animate for LengthPercentage {
927 #[inline]
928 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
929 Ok(match (self.unpack(), other.unpack()) {
930 (Unpacked::Length(one), Unpacked::Length(other)) => {
931 Self::new_length(one.animate(&other, procedure)?)
932 },
933 (Unpacked::Percentage(one), Unpacked::Percentage(other)) => {
934 Self::new_percent(one.animate(&other, procedure)?)
935 },
936 _ => {
937 use calc::CalcNodeLeaf;
938
939 fn product_with(mut node: CalcNode, product: f32) -> CalcNode {
940 let mut number = CalcNode::Leaf(ComputedLeaf::new_number(product));
941 if !node.try_product_in_place(&mut number) {
942 CalcNode::Product(vec![node, number].into())
943 } else {
944 node
945 }
946 }
947
948 let (l, r) = procedure.weights();
949 let one = product_with(self.to_calc_node(), l as f32);
950 let other = product_with(other.to_calc_node(), r as f32);
951
952 Self::new_calc(
953 CalcNode::Sum(vec![one, other].into()),
954 AllowedNumericType::All,
955 )
956 },
957 })
958 }
959}
960
961pub type NonNegativeLengthPercentage = NonNegative<LengthPercentage>;
963
964impl NonNegativeLengthPercentage {
965 #[inline]
967 pub fn to_used_value(&self, containing_length: Au) -> Au {
968 let resolved = self.0.to_used_value(containing_length);
969 std::cmp::max(resolved, Au(0))
970 }
971
972 #[inline]
974 pub fn maybe_to_used_value(&self, containing_length: Option<Au>) -> Option<Au> {
975 let resolved = self.0.maybe_to_used_value(containing_length)?;
976 Some(std::cmp::max(resolved, Au(0)))
977 }
978}
979
980impl TryTacticAdjustment for LengthPercentage {
981 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
982 match self.unpack_mut() {
983 UnpackedMut::Calc(calc) => calc.node.try_tactic_adjustment(old_side, new_side),
984 UnpackedMut::Percentage(mut p) => {
985 p.try_tactic_adjustment(old_side, new_side);
986 *self = Self::new_percent(p);
987 },
988 UnpackedMut::Length(..) => {},
989 }
990 }
991}
992
993impl TryTacticAdjustment for GenericAnchorFunctionFallback<ComputedLeaf> {
994 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
995 self.node.try_tactic_adjustment(old_side, new_side)
996 }
997}
998
999impl TryTacticAdjustment for CalcNode {
1000 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
1001 self.visit_depth_first(|node| match node {
1002 Self::Leaf(ComputedLeaf::Percentage(p)) => p.try_tactic_adjustment(old_side, new_side),
1003 Self::Anchor(a) => a.try_tactic_adjustment(old_side, new_side),
1004 Self::AnchorSize(a) => a.try_tactic_adjustment(old_side, new_side),
1005 _ => {},
1006 });
1007 }
1008}