style/values/computed/
box.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Computed types for box properties.
6
7use crate::values::animated::{Animate, Procedure, ToAnimatedValue};
8use crate::values::computed::font::FixedPoint;
9use crate::values::computed::length::{LengthPercentage, NonNegativeLength};
10use crate::values::computed::{Context, Integer, Number, ToComputedValue};
11use crate::values::generics::box_::{
12    GenericContainIntrinsicSize, GenericLineClamp, GenericPerspective, GenericVerticalAlign,
13};
14use crate::values::specified::box_ as specified;
15use std::fmt;
16use style_traits::{CssWriter, ToCss};
17
18pub use crate::values::specified::box_::{
19    Appearance, BaselineSource, BreakBetween, BreakWithin, Clear, Contain, ContainerName,
20    ContainerType, ContentVisibility, Display, Float, Overflow, OverflowAnchor, OverflowClipBox,
21    OverscrollBehavior, PositionProperty, ScrollSnapAlign, ScrollSnapAxis, ScrollSnapStop,
22    ScrollSnapStrictness, ScrollSnapType, ScrollbarGutter, TouchAction, WillChange,
23    WritingModeProperty,
24};
25
26/// A computed value for the `vertical-align` property.
27pub type VerticalAlign = GenericVerticalAlign<LengthPercentage>;
28
29/// A computed value for the `contain-intrinsic-size` property.
30pub type ContainIntrinsicSize = GenericContainIntrinsicSize<NonNegativeLength>;
31
32impl ContainIntrinsicSize {
33    /// Converts contain-intrinsic-size to auto style.
34    pub fn add_auto_if_needed(&self) -> Option<Self> {
35        Some(match *self {
36            Self::None => Self::AutoNone,
37            Self::Length(ref l) => Self::AutoLength(*l),
38            Self::AutoNone | Self::AutoLength(..) => return None,
39        })
40    }
41}
42
43/// A computed value for the `line-clamp` property.
44pub type LineClamp = GenericLineClamp<Integer>;
45
46impl Animate for LineClamp {
47    #[inline]
48    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
49        if self.is_none() != other.is_none() {
50            return Err(());
51        }
52        if self.is_none() {
53            return Ok(Self::none());
54        }
55        Ok(Self(self.0.animate(&other.0, procedure)?.max(1)))
56    }
57}
58
59/// A computed value for the `perspective` property.
60pub type Perspective = GenericPerspective<NonNegativeLength>;
61
62/// A computed value for the `resize` property.
63#[allow(missing_docs)]
64#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
65#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, ToCss, ToResolvedValue)]
66#[repr(u8)]
67pub enum Resize {
68    None,
69    Both,
70    Horizontal,
71    Vertical,
72}
73
74impl ToComputedValue for specified::Resize {
75    type ComputedValue = Resize;
76
77    #[inline]
78    fn to_computed_value(&self, context: &Context) -> Resize {
79        let is_vertical = context.style().writing_mode.is_vertical();
80        match self {
81            specified::Resize::Inline => {
82                context
83                    .rule_cache_conditions
84                    .borrow_mut()
85                    .set_writing_mode_dependency(context.builder.writing_mode);
86                if is_vertical {
87                    Resize::Vertical
88                } else {
89                    Resize::Horizontal
90                }
91            },
92            specified::Resize::Block => {
93                context
94                    .rule_cache_conditions
95                    .borrow_mut()
96                    .set_writing_mode_dependency(context.builder.writing_mode);
97                if is_vertical {
98                    Resize::Horizontal
99                } else {
100                    Resize::Vertical
101                }
102            },
103            specified::Resize::None => Resize::None,
104            specified::Resize::Both => Resize::Both,
105            specified::Resize::Horizontal => Resize::Horizontal,
106            specified::Resize::Vertical => Resize::Vertical,
107        }
108    }
109
110    #[inline]
111    fn from_computed_value(computed: &Resize) -> specified::Resize {
112        match computed {
113            Resize::None => specified::Resize::None,
114            Resize::Both => specified::Resize::Both,
115            Resize::Horizontal => specified::Resize::Horizontal,
116            Resize::Vertical => specified::Resize::Vertical,
117        }
118    }
119}
120
121/// We use an unsigned 10.6 fixed-point value (range 0.0 - 1023.984375).
122pub const ZOOM_FRACTION_BITS: u16 = 6;
123
124/// This is an alias which is useful mostly as a cbindgen / C++ inference workaround.
125pub type ZoomFixedPoint = FixedPoint<u16, ZOOM_FRACTION_BITS>;
126
127/// The computed `zoom` property value. We store it as a 16-bit fixed point because we need to
128/// store it efficiently in the ComputedStyle representation. The assumption being that zooms over
129/// 1000 aren't quite useful.
130#[derive(
131    Clone,
132    ComputeSquaredDistance,
133    Copy,
134    Debug,
135    Hash,
136    MallocSizeOf,
137    PartialEq,
138    PartialOrd,
139    ToResolvedValue,
140)]
141#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
142#[repr(C)]
143pub struct Zoom(ZoomFixedPoint);
144
145impl ToComputedValue for specified::Zoom {
146    type ComputedValue = Zoom;
147
148    #[inline]
149    fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
150        let n = match *self {
151            Self::Normal => return Zoom::ONE,
152            Self::Document => return Zoom::DOCUMENT,
153            Self::Value(ref n) => n.0.to_number().get(),
154        };
155        if n == 0.0 {
156            // For legacy reasons, zoom: 0 (and 0%) computes to 1. ¯\_(ツ)_/¯
157            return Zoom::ONE;
158        }
159        Zoom(ZoomFixedPoint::from_float(n))
160    }
161
162    #[inline]
163    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
164        Self::new_number(computed.value())
165    }
166}
167
168impl ToCss for Zoom {
169    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
170    where
171        W: fmt::Write,
172    {
173        use std::fmt::Write;
174        if *self == Self::DOCUMENT {
175            return dest.write_str("document");
176        }
177        self.value().to_css(dest)
178    }
179}
180
181impl ToAnimatedValue for Zoom {
182    type AnimatedValue = Number;
183
184    #[inline]
185    fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
186        self.value()
187    }
188
189    #[inline]
190    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
191        Zoom(ZoomFixedPoint::from_float(animated.max(0.0)))
192    }
193}
194
195impl Zoom {
196    /// The value 1. This is by far the most common value.
197    pub const ONE: Zoom = Zoom(ZoomFixedPoint {
198        value: 1 << ZOOM_FRACTION_BITS,
199    });
200
201    /// The `document` value. This can appear in the computed zoom property value, but not in the
202    /// `effective_zoom` field.
203    pub const DOCUMENT: Zoom = Zoom(ZoomFixedPoint { value: 0 });
204
205    /// Returns whether we're the number 1.
206    #[inline]
207    pub fn is_one(self) -> bool {
208        self == Self::ONE
209    }
210
211    /// Returns whether we're the `document` keyword.
212    #[inline]
213    pub fn is_document(self) -> bool {
214        self == Self::DOCUMENT
215    }
216
217    /// Returns the inverse of our value.
218    #[inline]
219    pub fn inverted(&self) -> Option<Self> {
220        if self.0.value == 0 {
221            return None;
222        }
223        Some(Self(Self::ONE.0 / self.0))
224    }
225
226    /// Returns the value as a float.
227    #[inline]
228    pub fn value(&self) -> f32 {
229        self.0.to_float()
230    }
231
232    /// Computes the effective zoom for a given new zoom value in rhs.
233    pub fn compute_effective(self, specified: Self) -> Self {
234        if specified == Self::DOCUMENT {
235            return Self::ONE;
236        }
237        if self == Self::ONE {
238            return specified;
239        }
240        if specified == Self::ONE {
241            return self;
242        }
243        Zoom(self.0 * specified.0)
244    }
245
246    /// Returns the zoomed value.
247    #[inline]
248    pub fn zoom(self, value: f32) -> f32 {
249        if self == Self::ONE {
250            return value;
251        }
252        value * self.value()
253    }
254
255    /// Returns the un-zoomed value.
256    #[inline]
257    pub fn unzoom(self, value: f32) -> f32 {
258        // Avoid division by zero if our effective zoom computation ends up being zero.
259        if self == Self::ONE || self.0.value == 0 {
260            return value;
261        }
262        value / self.value()
263    }
264}