summaryrefslogtreecommitdiffstats
path: root/servo/components/style/values/computed
diff options
context:
space:
mode:
Diffstat (limited to 'servo/components/style/values/computed')
-rw-r--r--servo/components/style/values/computed/align.rs91
-rw-r--r--servo/components/style/values/computed/angle.rs101
-rw-r--r--servo/components/style/values/computed/animation.rs70
-rw-r--r--servo/components/style/values/computed/background.rs13
-rw-r--r--servo/components/style/values/computed/basic_shape.rs37
-rw-r--r--servo/components/style/values/computed/border.rs84
-rw-r--r--servo/components/style/values/computed/box.rs388
-rw-r--r--servo/components/style/values/computed/color.rs95
-rw-r--r--servo/components/style/values/computed/column.rs11
-rw-r--r--servo/components/style/values/computed/counters.rs26
-rw-r--r--servo/components/style/values/computed/easing.rs109
-rw-r--r--servo/components/style/values/computed/effects.rs44
-rw-r--r--servo/components/style/values/computed/flex.rs19
-rw-r--r--servo/components/style/values/computed/font.rs1369
-rw-r--r--servo/components/style/values/computed/image.rs205
-rw-r--r--servo/components/style/values/computed/length.rs531
-rw-r--r--servo/components/style/values/computed/length_percentage.rs1055
-rw-r--r--servo/components/style/values/computed/list.rs17
-rw-r--r--servo/components/style/values/computed/mod.rs1035
-rw-r--r--servo/components/style/values/computed/motion.rs70
-rw-r--r--servo/components/style/values/computed/outline.rs7
-rw-r--r--servo/components/style/values/computed/page.rs75
-rw-r--r--servo/components/style/values/computed/percentage.rs136
-rw-r--r--servo/components/style/values/computed/position.rs74
-rw-r--r--servo/components/style/values/computed/ratio.rs115
-rw-r--r--servo/components/style/values/computed/rect.rs11
-rw-r--r--servo/components/style/values/computed/resolution.rs56
-rw-r--r--servo/components/style/values/computed/svg.rs66
-rw-r--r--servo/components/style/values/computed/table.rs7
-rw-r--r--servo/components/style/values/computed/text.rs228
-rw-r--r--servo/components/style/values/computed/time.rs45
-rw-r--r--servo/components/style/values/computed/transform.rs559
-rw-r--r--servo/components/style/values/computed/ui.rs21
-rw-r--r--servo/components/style/values/computed/url.rs15
34 files changed, 6785 insertions, 0 deletions
diff --git a/servo/components/style/values/computed/align.rs b/servo/components/style/values/computed/align.rs
new file mode 100644
index 0000000000..94847fd80f
--- /dev/null
+++ b/servo/components/style/values/computed/align.rs
@@ -0,0 +1,91 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Values for CSS Box Alignment properties
+//!
+//! https://drafts.csswg.org/css-align/
+
+use crate::values::computed::{Context, ToComputedValue};
+use crate::values::specified;
+
+pub use super::specified::{
+ AlignContent, AlignItems, AlignTracks, ContentDistribution, JustifyContent, JustifyTracks,
+ SelfAlignment,
+};
+pub use super::specified::{AlignSelf, JustifySelf};
+
+/// The computed value for the `justify-items` property.
+///
+/// Need to carry around both the specified and computed value to handle the
+/// special legacy keyword without destroying style sharing.
+///
+/// In particular, `justify-items` is a reset property, so we ought to be able
+/// to share its computed representation across elements as long as they match
+/// the same rules. Except that it's not true if the specified value for
+/// `justify-items` is `legacy` and the computed value of the parent has the
+/// `legacy` modifier.
+///
+/// So instead of computing `legacy` "normally" looking at get_parent_position(),
+/// marking it as uncacheable, we carry the specified value around and handle
+/// the special case in `StyleAdjuster` instead, only when the result of the
+/// computation would vary.
+///
+/// Note that we also need to special-case this property in matching.rs, in
+/// order to properly handle changes to the legacy keyword... This all kinda
+/// sucks :(.
+///
+/// See the discussion in https://bugzil.la/1384542.
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToResolvedValue)]
+#[repr(C)]
+pub struct ComputedJustifyItems {
+ /// The specified value for the property. Can contain the bare `legacy`
+ /// keyword.
+ #[css(skip)]
+ pub specified: specified::JustifyItems,
+ /// The computed value for the property. Cannot contain the bare `legacy`
+ /// keyword, but note that it could contain it in combination with other
+ /// keywords like `left`, `right` or `center`.
+ pub computed: specified::JustifyItems,
+}
+
+pub use self::ComputedJustifyItems as JustifyItems;
+
+impl JustifyItems {
+ /// Returns the `legacy` value.
+ pub fn legacy() -> Self {
+ Self {
+ specified: specified::JustifyItems::legacy(),
+ computed: specified::JustifyItems::normal(),
+ }
+ }
+}
+
+impl ToComputedValue for specified::JustifyItems {
+ type ComputedValue = JustifyItems;
+
+ /// <https://drafts.csswg.org/css-align/#valdef-justify-items-legacy>
+ fn to_computed_value(&self, _context: &Context) -> JustifyItems {
+ use crate::values::specified::align;
+ let specified = *self;
+ let computed = if self.0 != align::AlignFlags::LEGACY {
+ *self
+ } else {
+ // If the inherited value of `justify-items` includes the
+ // `legacy` keyword, `legacy` computes to the inherited value, but
+ // we assume it computes to `normal`, and handle that special-case
+ // in StyleAdjuster.
+ Self::normal()
+ };
+
+ JustifyItems {
+ specified,
+ computed,
+ }
+ }
+
+ #[inline]
+ fn from_computed_value(computed: &JustifyItems) -> Self {
+ computed.specified
+ }
+}
diff --git a/servo/components/style/values/computed/angle.rs b/servo/components/style/values/computed/angle.rs
new file mode 100644
index 0000000000..ea321d2233
--- /dev/null
+++ b/servo/components/style/values/computed/angle.rs
@@ -0,0 +1,101 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Computed angles.
+
+use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
+use crate::values::CSSFloat;
+use crate::Zero;
+use std::f64::consts::PI;
+use std::fmt::{self, Write};
+use std::{f32, f64};
+use style_traits::{CssWriter, ToCss};
+
+/// A computed angle in degrees.
+#[derive(
+ Add,
+ Animate,
+ Clone,
+ Copy,
+ Debug,
+ Deserialize,
+ MallocSizeOf,
+ PartialEq,
+ PartialOrd,
+ Serialize,
+ ToAnimatedZero,
+ ToResolvedValue,
+)]
+#[repr(C)]
+pub struct Angle(CSSFloat);
+
+impl ToCss for Angle {
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: Write,
+ {
+ self.degrees().to_css(dest)?;
+ dest.write_str("deg")
+ }
+}
+
+const RAD_PER_DEG: f64 = PI / 180.0;
+
+impl Angle {
+ /// Creates a computed `Angle` value from a radian amount.
+ pub fn from_radians(radians: CSSFloat) -> Self {
+ Angle(radians / RAD_PER_DEG as f32)
+ }
+
+ /// Creates a computed `Angle` value from a degrees amount.
+ #[inline]
+ pub fn from_degrees(degrees: CSSFloat) -> Self {
+ Angle(degrees)
+ }
+
+ /// Returns the amount of radians this angle represents.
+ #[inline]
+ pub fn radians(&self) -> CSSFloat {
+ self.radians64().min(f32::MAX as f64).max(f32::MIN as f64) as f32
+ }
+
+ /// Returns the amount of radians this angle represents as a `f64`.
+ ///
+ /// Gecko stores angles as singles, but does this computation using doubles.
+ ///
+ /// This is significant enough to mess up rounding to the nearest
+ /// quarter-turn for 225 degrees, for example.
+ #[inline]
+ pub fn radians64(&self) -> f64 {
+ self.0 as f64 * RAD_PER_DEG
+ }
+
+ /// Return the value in degrees.
+ #[inline]
+ pub fn degrees(&self) -> CSSFloat {
+ self.0
+ }
+}
+
+impl Zero for Angle {
+ #[inline]
+ fn zero() -> Self {
+ Angle(0.0)
+ }
+
+ #[inline]
+ fn is_zero(&self) -> bool {
+ self.0 == 0.
+ }
+}
+
+impl ComputeSquaredDistance for Angle {
+ #[inline]
+ fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
+ // Use the formula for calculating the distance between angles defined in SVG:
+ // https://www.w3.org/TR/SVG/animate.html#complexDistances
+ self.radians64()
+ .compute_squared_distance(&other.radians64())
+ }
+}
diff --git a/servo/components/style/values/computed/animation.rs b/servo/components/style/values/computed/animation.rs
new file mode 100644
index 0000000000..626dbe5347
--- /dev/null
+++ b/servo/components/style/values/computed/animation.rs
@@ -0,0 +1,70 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Computed values for properties related to animations and transitions
+
+use crate::values::computed::{Context, LengthPercentage, ToComputedValue};
+use crate::values::generics::animation as generics;
+use crate::values::specified::animation as specified;
+use std::fmt::{self, Write};
+use style_traits::{CssWriter, ToCss};
+
+pub use crate::values::specified::animation::{
+ AnimationName, ScrollAxis, ScrollTimelineName, TransitionProperty, AnimationComposition,
+ AnimationDirection, AnimationFillMode, AnimationPlayState,
+};
+
+/// A computed value for the `animation-iteration-count` property.
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToResolvedValue, ToShmem)]
+#[repr(C)]
+pub struct AnimationIterationCount(pub f32);
+
+impl ToComputedValue for specified::AnimationIterationCount {
+ type ComputedValue = AnimationIterationCount;
+
+ #[inline]
+ fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+ AnimationIterationCount(match *self {
+ specified::AnimationIterationCount::Number(n) => n.to_computed_value(context).0,
+ specified::AnimationIterationCount::Infinite => f32::INFINITY,
+ })
+ }
+
+ #[inline]
+ fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+ use crate::values::specified::NonNegativeNumber;
+ if computed.0.is_infinite() {
+ specified::AnimationIterationCount::Infinite
+ } else {
+ specified::AnimationIterationCount::Number(NonNegativeNumber::new(computed.0))
+ }
+ }
+}
+
+impl AnimationIterationCount {
+ /// Returns the value `1.0`.
+ #[inline]
+ pub fn one() -> Self {
+ Self(1.0)
+ }
+}
+
+impl ToCss for AnimationIterationCount {
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: Write,
+ {
+ if self.0.is_infinite() {
+ dest.write_str("infinite")
+ } else {
+ self.0.to_css(dest)
+ }
+ }
+}
+
+/// A computed value for the `animation-timeline` property.
+pub type AnimationTimeline = generics::GenericAnimationTimeline<LengthPercentage>;
+
+/// A computed value for the `view-timeline-inset` property.
+pub type ViewTimelineInset = generics::GenericViewTimelineInset<LengthPercentage>;
diff --git a/servo/components/style/values/computed/background.rs b/servo/components/style/values/computed/background.rs
new file mode 100644
index 0000000000..e2a58f8b74
--- /dev/null
+++ b/servo/components/style/values/computed/background.rs
@@ -0,0 +1,13 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Computed types for CSS values related to backgrounds.
+
+use crate::values::computed::length::NonNegativeLengthPercentage;
+use crate::values::generics::background::BackgroundSize as GenericBackgroundSize;
+
+pub use crate::values::specified::background::BackgroundRepeat;
+
+/// A computed value for the `background-size` property.
+pub type BackgroundSize = GenericBackgroundSize<NonNegativeLengthPercentage>;
diff --git a/servo/components/style/values/computed/basic_shape.rs b/servo/components/style/values/computed/basic_shape.rs
new file mode 100644
index 0000000000..d39110ec1c
--- /dev/null
+++ b/servo/components/style/values/computed/basic_shape.rs
@@ -0,0 +1,37 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! CSS handling for the computed value of
+//! [`basic-shape`][basic-shape]s
+//!
+//! [basic-shape]: https://drafts.csswg.org/css-shapes/#typedef-basic-shape
+
+use crate::values::computed::url::ComputedUrl;
+use crate::values::computed::{Image, LengthPercentage, NonNegativeLengthPercentage, Position};
+use crate::values::generics::basic_shape as generic;
+
+/// A computed alias for FillRule.
+pub use crate::values::generics::basic_shape::FillRule;
+
+/// A computed `clip-path` value.
+pub type ClipPath = generic::GenericClipPath<BasicShape, ComputedUrl>;
+
+/// A computed `shape-outside` value.
+pub type ShapeOutside = generic::GenericShapeOutside<BasicShape, Image>;
+
+/// A computed basic shape.
+pub type BasicShape =
+ generic::GenericBasicShape<Position, LengthPercentage, NonNegativeLengthPercentage, InsetRect>;
+
+/// The computed value of `inset()`.
+pub type InsetRect = generic::GenericInsetRect<LengthPercentage, NonNegativeLengthPercentage>;
+
+/// A computed circle.
+pub type Circle = generic::Circle<Position, NonNegativeLengthPercentage>;
+
+/// A computed ellipse.
+pub type Ellipse = generic::Ellipse<Position, NonNegativeLengthPercentage>;
+
+/// The computed value of `ShapeRadius`.
+pub type ShapeRadius = generic::GenericShapeRadius<NonNegativeLengthPercentage>;
diff --git a/servo/components/style/values/computed/border.rs b/servo/components/style/values/computed/border.rs
new file mode 100644
index 0000000000..e073f671b3
--- /dev/null
+++ b/servo/components/style/values/computed/border.rs
@@ -0,0 +1,84 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Computed types for CSS values related to borders.
+
+use crate::values::computed::length::{NonNegativeLength, NonNegativeLengthPercentage};
+use crate::values::computed::{NonNegativeNumber, NonNegativeNumberOrPercentage};
+use crate::values::generics::border::BorderCornerRadius as GenericBorderCornerRadius;
+use crate::values::generics::border::BorderImageSlice as GenericBorderImageSlice;
+use crate::values::generics::border::BorderRadius as GenericBorderRadius;
+use crate::values::generics::border::BorderSpacing as GenericBorderSpacing;
+use crate::values::generics::border::GenericBorderImageSideWidth;
+use crate::values::generics::rect::Rect;
+use crate::values::generics::size::Size2D;
+use crate::values::generics::NonNegative;
+use crate::Zero;
+use app_units::Au;
+
+pub use crate::values::specified::border::BorderImageRepeat;
+
+/// A computed value for -webkit-text-stroke-width.
+pub type LineWidth = Au;
+
+/// A computed value for border-width (and the like).
+pub type BorderSideWidth = Au;
+
+/// A computed value for the `border-image-width` property.
+pub type BorderImageWidth = Rect<BorderImageSideWidth>;
+
+/// A computed value for a single side of a `border-image-width` property.
+pub type BorderImageSideWidth =
+ GenericBorderImageSideWidth<NonNegativeLengthPercentage, NonNegativeNumber>;
+
+/// A computed value for the `border-image-slice` property.
+pub type BorderImageSlice = GenericBorderImageSlice<NonNegativeNumberOrPercentage>;
+
+/// A computed value for the `border-radius` property.
+pub type BorderRadius = GenericBorderRadius<NonNegativeLengthPercentage>;
+
+/// A computed value for the `border-*-radius` longhand properties.
+pub type BorderCornerRadius = GenericBorderCornerRadius<NonNegativeLengthPercentage>;
+
+/// A computed value for the `border-spacing` longhand property.
+pub type BorderSpacing = GenericBorderSpacing<NonNegativeLength>;
+
+impl BorderImageSideWidth {
+ /// Returns `1`.
+ #[inline]
+ pub fn one() -> Self {
+ GenericBorderImageSideWidth::Number(NonNegative(1.))
+ }
+}
+
+impl BorderImageSlice {
+ /// Returns the `100%` value.
+ #[inline]
+ pub fn hundred_percent() -> Self {
+ GenericBorderImageSlice {
+ offsets: Rect::all(NonNegativeNumberOrPercentage::hundred_percent()),
+ fill: false,
+ }
+ }
+}
+
+impl BorderSpacing {
+ /// Returns `0 0`.
+ pub fn zero() -> Self {
+ GenericBorderSpacing(Size2D::new(
+ NonNegativeLength::zero(),
+ NonNegativeLength::zero(),
+ ))
+ }
+
+ /// Returns the horizontal spacing.
+ pub fn horizontal(&self) -> Au {
+ Au::from(*self.0.width())
+ }
+
+ /// Returns the vertical spacing.
+ pub fn vertical(&self) -> Au {
+ Au::from(*self.0.height())
+ }
+}
diff --git a/servo/components/style/values/computed/box.rs b/servo/components/style/values/computed/box.rs
new file mode 100644
index 0000000000..62811d9851
--- /dev/null
+++ b/servo/components/style/values/computed/box.rs
@@ -0,0 +1,388 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Computed types for box properties.
+
+use crate::values::animated::{Animate, Procedure, ToAnimatedValue};
+use crate::values::computed::font::FixedPoint;
+use crate::values::computed::length::{LengthPercentage, NonNegativeLength};
+use crate::values::computed::{Context, Integer, Number, ToComputedValue};
+use crate::values::generics::box_::{
+ GenericContainIntrinsicSize, GenericLineClamp, GenericPerspective, GenericVerticalAlign,
+};
+use crate::values::specified::box_ as specified;
+use std::fmt;
+use style_traits::{CssWriter, ToCss};
+
+pub use crate::values::specified::box_::{
+ Appearance, BaselineSource, BreakBetween, BreakWithin, Clear as SpecifiedClear, Contain,
+ ContainerName, ContainerType, ContentVisibility, Display, Float as SpecifiedFloat, Overflow,
+ OverflowAnchor, OverflowClipBox, OverscrollBehavior, ScrollSnapAlign, ScrollSnapAxis,
+ ScrollSnapStop, ScrollSnapStrictness, ScrollSnapType, ScrollbarGutter, TouchAction, WillChange,
+};
+
+/// A computed value for the `vertical-align` property.
+pub type VerticalAlign = GenericVerticalAlign<LengthPercentage>;
+
+/// A computed value for the `contain-intrinsic-size` property.
+pub type ContainIntrinsicSize = GenericContainIntrinsicSize<NonNegativeLength>;
+
+impl ContainIntrinsicSize {
+ /// Converts contain-intrinsic-size to auto style.
+ pub fn add_auto_if_needed(&self) -> Option<Self> {
+ Some(match *self {
+ Self::None => Self::AutoNone,
+ Self::Length(ref l) => Self::AutoLength(*l),
+ Self::AutoNone | Self::AutoLength(..) => return None,
+ })
+ }
+}
+
+/// A computed value for the `line-clamp` property.
+pub type LineClamp = GenericLineClamp<Integer>;
+
+impl Animate for LineClamp {
+ #[inline]
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+ if self.is_none() != other.is_none() {
+ return Err(());
+ }
+ if self.is_none() {
+ return Ok(Self::none());
+ }
+ Ok(Self(self.0.animate(&other.0, procedure)?.max(1)))
+ }
+}
+
+/// A computed value for the `perspective` property.
+pub type Perspective = GenericPerspective<NonNegativeLength>;
+
+#[allow(missing_docs)]
+#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+#[derive(
+ Clone,
+ Copy,
+ Debug,
+ Eq,
+ FromPrimitive,
+ Hash,
+ MallocSizeOf,
+ Parse,
+ PartialEq,
+ SpecifiedValueInfo,
+ ToCss,
+ ToResolvedValue,
+)]
+#[repr(u8)]
+/// A computed value for the `float` property.
+pub enum Float {
+ Left,
+ Right,
+ None,
+}
+
+impl Float {
+ /// Returns true if `self` is not `None`.
+ pub fn is_floating(self) -> bool {
+ self != Self::None
+ }
+}
+
+impl ToComputedValue for SpecifiedFloat {
+ type ComputedValue = Float;
+
+ #[inline]
+ fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+ let ltr = context.style().writing_mode.is_bidi_ltr();
+ // https://drafts.csswg.org/css-logical-props/#float-clear
+ match *self {
+ SpecifiedFloat::InlineStart => {
+ context
+ .rule_cache_conditions
+ .borrow_mut()
+ .set_writing_mode_dependency(context.builder.writing_mode);
+ if ltr {
+ Float::Left
+ } else {
+ Float::Right
+ }
+ },
+ SpecifiedFloat::InlineEnd => {
+ context
+ .rule_cache_conditions
+ .borrow_mut()
+ .set_writing_mode_dependency(context.builder.writing_mode);
+ if ltr {
+ Float::Right
+ } else {
+ Float::Left
+ }
+ },
+ SpecifiedFloat::Left => Float::Left,
+ SpecifiedFloat::Right => Float::Right,
+ SpecifiedFloat::None => Float::None,
+ }
+ }
+
+ #[inline]
+ fn from_computed_value(computed: &Self::ComputedValue) -> SpecifiedFloat {
+ match *computed {
+ Float::Left => SpecifiedFloat::Left,
+ Float::Right => SpecifiedFloat::Right,
+ Float::None => SpecifiedFloat::None,
+ }
+ }
+}
+
+#[allow(missing_docs)]
+#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+#[derive(
+ Clone,
+ Copy,
+ Debug,
+ Eq,
+ FromPrimitive,
+ Hash,
+ MallocSizeOf,
+ Parse,
+ PartialEq,
+ SpecifiedValueInfo,
+ ToCss,
+ ToResolvedValue,
+)]
+/// A computed value for the `clear` property.
+#[repr(u8)]
+pub enum Clear {
+ None,
+ Left,
+ Right,
+ Both,
+}
+
+impl ToComputedValue for SpecifiedClear {
+ type ComputedValue = Clear;
+
+ #[inline]
+ fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+ let ltr = context.style().writing_mode.is_bidi_ltr();
+ // https://drafts.csswg.org/css-logical-props/#float-clear
+ match *self {
+ SpecifiedClear::InlineStart => {
+ context
+ .rule_cache_conditions
+ .borrow_mut()
+ .set_writing_mode_dependency(context.builder.writing_mode);
+ if ltr {
+ Clear::Left
+ } else {
+ Clear::Right
+ }
+ },
+ SpecifiedClear::InlineEnd => {
+ context
+ .rule_cache_conditions
+ .borrow_mut()
+ .set_writing_mode_dependency(context.builder.writing_mode);
+ if ltr {
+ Clear::Right
+ } else {
+ Clear::Left
+ }
+ },
+ SpecifiedClear::None => Clear::None,
+ SpecifiedClear::Left => Clear::Left,
+ SpecifiedClear::Right => Clear::Right,
+ SpecifiedClear::Both => Clear::Both,
+ }
+ }
+
+ #[inline]
+ fn from_computed_value(computed: &Self::ComputedValue) -> SpecifiedClear {
+ match *computed {
+ Clear::None => SpecifiedClear::None,
+ Clear::Left => SpecifiedClear::Left,
+ Clear::Right => SpecifiedClear::Right,
+ Clear::Both => SpecifiedClear::Both,
+ }
+ }
+}
+
+/// A computed value for the `resize` property.
+#[allow(missing_docs)]
+#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, ToCss, ToResolvedValue)]
+#[repr(u8)]
+pub enum Resize {
+ None,
+ Both,
+ Horizontal,
+ Vertical,
+}
+
+impl ToComputedValue for specified::Resize {
+ type ComputedValue = Resize;
+
+ #[inline]
+ fn to_computed_value(&self, context: &Context) -> Resize {
+ let is_vertical = context.style().writing_mode.is_vertical();
+ match self {
+ specified::Resize::Inline => {
+ context
+ .rule_cache_conditions
+ .borrow_mut()
+ .set_writing_mode_dependency(context.builder.writing_mode);
+ if is_vertical {
+ Resize::Vertical
+ } else {
+ Resize::Horizontal
+ }
+ },
+ specified::Resize::Block => {
+ context
+ .rule_cache_conditions
+ .borrow_mut()
+ .set_writing_mode_dependency(context.builder.writing_mode);
+ if is_vertical {
+ Resize::Horizontal
+ } else {
+ Resize::Vertical
+ }
+ },
+ specified::Resize::None => Resize::None,
+ specified::Resize::Both => Resize::Both,
+ specified::Resize::Horizontal => Resize::Horizontal,
+ specified::Resize::Vertical => Resize::Vertical,
+ }
+ }
+
+ #[inline]
+ fn from_computed_value(computed: &Resize) -> specified::Resize {
+ match computed {
+ Resize::None => specified::Resize::None,
+ Resize::Both => specified::Resize::Both,
+ Resize::Horizontal => specified::Resize::Horizontal,
+ Resize::Vertical => specified::Resize::Vertical,
+ }
+ }
+}
+
+/// We use an unsigned 10.6 fixed-point value (range 0.0 - 1023.984375).
+pub const ZOOM_FRACTION_BITS: u16 = 6;
+
+/// This is an alias which is useful mostly as a cbindgen / C++ inference workaround.
+pub type ZoomFixedPoint = FixedPoint<u16, ZOOM_FRACTION_BITS>;
+
+/// The computed `zoom` property value. We store it as a 16-bit fixed point because we need to
+/// store it efficiently in the ComputedStyle representation. The assumption being that zooms over
+/// 1000 aren't quite useful.
+#[derive(
+ Clone,
+ ComputeSquaredDistance,
+ Copy,
+ Debug,
+ Hash,
+ MallocSizeOf,
+ PartialEq,
+ PartialOrd,
+ ToResolvedValue,
+)]
+#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+#[repr(C)]
+pub struct Zoom(ZoomFixedPoint);
+
+impl ToComputedValue for specified::Zoom {
+ type ComputedValue = Zoom;
+
+ #[inline]
+ fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
+ let n = match *self {
+ Self::Normal => return Zoom::ONE,
+ Self::Document => return Zoom::DOCUMENT,
+ Self::Value(ref n) => n.0.to_number().get(),
+ };
+ if n == 0.0 {
+ // For legacy reasons, zoom: 0 (and 0%) computes to 1. ¯\_(ツ)_/¯
+ return Zoom::ONE;
+ }
+ Zoom(ZoomFixedPoint::from_float(n))
+ }
+
+ #[inline]
+ fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+ Self::new_number(computed.value())
+ }
+}
+
+impl ToCss for Zoom {
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: fmt::Write,
+ {
+ use std::fmt::Write;
+ if *self == Self::DOCUMENT {
+ return dest.write_str("document");
+ }
+ self.value().to_css(dest)
+ }
+}
+
+impl ToAnimatedValue for Zoom {
+ type AnimatedValue = Number;
+
+ #[inline]
+ fn to_animated_value(self) -> Self::AnimatedValue {
+ self.value()
+ }
+
+ #[inline]
+ fn from_animated_value(animated: Self::AnimatedValue) -> Self {
+ Zoom(ZoomFixedPoint::from_float(animated.max(0.0)))
+ }
+}
+
+impl Zoom {
+ /// The value 1. This is by far the most common value.
+ pub const ONE: Zoom = Zoom(ZoomFixedPoint {
+ value: 1 << ZOOM_FRACTION_BITS,
+ });
+
+ /// The `document` value. This can appear in the computed zoom property value, but not in the
+ /// `effective_zoom` field.
+ pub const DOCUMENT: Zoom = Zoom(ZoomFixedPoint { value: 0 });
+
+ /// Returns whether we're the number 1.
+ #[inline]
+ pub fn is_one(self) -> bool {
+ self == Self::ONE
+ }
+
+ /// Returns the value as a float.
+ #[inline]
+ pub fn value(&self) -> f32 {
+ self.0.to_float()
+ }
+
+ /// Computes the effective zoom for a given new zoom value in rhs.
+ pub fn compute_effective(self, specified: Self) -> Self {
+ if specified == Self::DOCUMENT {
+ return Self::ONE;
+ }
+ if self == Self::ONE {
+ return specified;
+ }
+ if specified == Self::ONE {
+ return self;
+ }
+ Zoom(self.0 * specified.0)
+ }
+
+ /// Returns the zoomed value.
+ #[inline]
+ pub fn zoom(self, value: f32) -> f32 {
+ if self == Self::ONE {
+ return value;
+ }
+ self.value() * value
+ }
+}
diff --git a/servo/components/style/values/computed/color.rs b/servo/components/style/values/computed/color.rs
new file mode 100644
index 0000000000..9b5185d923
--- /dev/null
+++ b/servo/components/style/values/computed/color.rs
@@ -0,0 +1,95 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Computed color values.
+
+use crate::color::parsing::Color as CSSParserColor;
+use crate::color::AbsoluteColor;
+use crate::values::animated::ToAnimatedZero;
+use crate::values::computed::percentage::Percentage;
+use crate::values::generics::color::{
+ GenericCaretColor, GenericColor, GenericColorMix, GenericColorOrAuto,
+};
+use std::fmt;
+use style_traits::{CssWriter, ToCss};
+
+pub use crate::values::specified::color::{ColorScheme, ForcedColorAdjust, PrintColorAdjust};
+
+/// The computed value of the `color` property.
+pub type ColorPropertyValue = AbsoluteColor;
+
+/// A computed value for `<color>`.
+pub type Color = GenericColor<Percentage>;
+
+/// A computed color-mix().
+pub type ColorMix = GenericColorMix<Color, Percentage>;
+
+impl ToCss for Color {
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: fmt::Write,
+ {
+ match *self {
+ Self::Absolute(ref c) => c.to_css(dest),
+ Self::CurrentColor => cssparser::ToCss::to_css(&CSSParserColor::CurrentColor, dest),
+ Self::ColorMix(ref m) => m.to_css(dest),
+ }
+ }
+}
+
+impl Color {
+ /// A fully transparent color.
+ pub const TRANSPARENT_BLACK: Self = Self::Absolute(AbsoluteColor::TRANSPARENT_BLACK);
+
+ /// An opaque black color.
+ pub const BLACK: Self = Self::Absolute(AbsoluteColor::BLACK);
+
+ /// An opaque white color.
+ pub const WHITE: Self = Self::Absolute(AbsoluteColor::WHITE);
+
+ /// Create a new computed [`Color`] from a given color-mix, simplifying it to an absolute color
+ /// if possible.
+ pub fn from_color_mix(color_mix: ColorMix) -> Self {
+ if let Some(absolute) = color_mix.mix_to_absolute() {
+ Self::Absolute(absolute)
+ } else {
+ Self::ColorMix(Box::new(color_mix))
+ }
+ }
+
+ /// Combine this complex color with the given foreground color into an
+ /// absolute color.
+ pub fn resolve_to_absolute(&self, current_color: &AbsoluteColor) -> AbsoluteColor {
+ use crate::values::specified::percentage::ToPercentage;
+
+ match *self {
+ Self::Absolute(c) => c,
+ Self::CurrentColor => *current_color,
+ Self::ColorMix(ref mix) => {
+ let left = mix.left.resolve_to_absolute(current_color);
+ let right = mix.right.resolve_to_absolute(current_color);
+ crate::color::mix::mix(
+ mix.interpolation,
+ &left,
+ mix.left_percentage.to_percentage(),
+ &right,
+ mix.right_percentage.to_percentage(),
+ mix.flags,
+ )
+ },
+ }
+ }
+}
+
+impl ToAnimatedZero for AbsoluteColor {
+ fn to_animated_zero(&self) -> Result<Self, ()> {
+ Ok(Self::TRANSPARENT_BLACK)
+ }
+}
+
+/// auto | <color>
+pub type ColorOrAuto = GenericColorOrAuto<Color>;
+
+/// caret-color
+pub type CaretColor = GenericCaretColor<Color>;
diff --git a/servo/components/style/values/computed/column.rs b/servo/components/style/values/computed/column.rs
new file mode 100644
index 0000000000..38437ea110
--- /dev/null
+++ b/servo/components/style/values/computed/column.rs
@@ -0,0 +1,11 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Computed types for the column properties.
+
+use crate::values::computed::PositiveInteger;
+use crate::values::generics::column::ColumnCount as GenericColumnCount;
+
+/// A computed type for `column-count` values.
+pub type ColumnCount = GenericColumnCount<PositiveInteger>;
diff --git a/servo/components/style/values/computed/counters.rs b/servo/components/style/values/computed/counters.rs
new file mode 100644
index 0000000000..fd5e915c4a
--- /dev/null
+++ b/servo/components/style/values/computed/counters.rs
@@ -0,0 +1,26 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Computed values for counter properties
+
+use crate::values::computed::image::Image;
+use crate::values::generics::counters as generics;
+use crate::values::generics::counters::CounterIncrement as GenericCounterIncrement;
+use crate::values::generics::counters::CounterReset as GenericCounterReset;
+use crate::values::generics::counters::CounterSet as GenericCounterSet;
+
+/// A computed value for the `counter-increment` property.
+pub type CounterIncrement = GenericCounterIncrement<i32>;
+
+/// A computed value for the `counter-reset` property.
+pub type CounterReset = GenericCounterReset<i32>;
+
+/// A computed value for the `counter-set` property.
+pub type CounterSet = GenericCounterSet<i32>;
+
+/// A computed value for the `content` property.
+pub type Content = generics::GenericContent<Image>;
+
+/// A computed content item.
+pub type ContentItem = generics::GenericContentItem<Image>;
diff --git a/servo/components/style/values/computed/easing.rs b/servo/components/style/values/computed/easing.rs
new file mode 100644
index 0000000000..d351b3c71d
--- /dev/null
+++ b/servo/components/style/values/computed/easing.rs
@@ -0,0 +1,109 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Computed types for CSS Easing functions.
+
+use euclid::approxeq::ApproxEq;
+
+use crate::bezier::Bezier;
+use crate::piecewise_linear::PiecewiseLinearFunction;
+use crate::values::computed::{Integer, Number};
+use crate::values::generics::easing::{self, BeforeFlag, StepPosition, TimingKeyword};
+
+/// A computed timing function.
+pub type ComputedTimingFunction = easing::TimingFunction<Integer, Number, PiecewiseLinearFunction>;
+
+/// An alias of the computed timing function.
+pub type TimingFunction = ComputedTimingFunction;
+
+impl ComputedTimingFunction {
+ fn calculate_step_output(
+ steps: i32,
+ pos: StepPosition,
+ progress: f64,
+ before_flag: BeforeFlag,
+ ) -> f64 {
+ // User specified values can cause overflow (bug 1706157). Increments/decrements
+ // should be gravefully handled.
+ let mut current_step = (progress * (steps as f64)).floor() as i32;
+
+ // Increment current step if it is jump-start or start.
+ if pos == StepPosition::Start ||
+ pos == StepPosition::JumpStart ||
+ pos == StepPosition::JumpBoth
+ {
+ current_step = current_step.checked_add(1).unwrap_or(current_step);
+ }
+
+ // If the "before flag" is set and we are at a transition point,
+ // drop back a step
+ if before_flag == BeforeFlag::Set &&
+ (progress * steps as f64).rem_euclid(1.0).approx_eq(&0.0)
+ {
+ current_step = current_step.checked_sub(1).unwrap_or(current_step);
+ }
+
+ // We should not produce a result outside [0, 1] unless we have an
+ // input outside that range. This takes care of steps that would otherwise
+ // occur at boundaries.
+ if progress >= 0.0 && current_step < 0 {
+ current_step = 0;
+ }
+
+ // |jumps| should always be in [1, i32::MAX].
+ let jumps = if pos == StepPosition::JumpBoth {
+ steps.checked_add(1).unwrap_or(steps)
+ } else if pos == StepPosition::JumpNone {
+ steps.checked_sub(1).unwrap_or(steps)
+ } else {
+ steps
+ };
+
+ if progress <= 1.0 && current_step > jumps {
+ current_step = jumps;
+ }
+
+ (current_step as f64) / (jumps as f64)
+ }
+
+ /// The output of the timing function given the progress ratio of this animation.
+ pub fn calculate_output(&self, progress: f64, before_flag: BeforeFlag, epsilon: f64) -> f64 {
+ let progress = match self {
+ TimingFunction::CubicBezier { x1, y1, x2, y2 } => {
+ Bezier::calculate_bezier_output(progress, epsilon, *x1, *y1, *x2, *y2)
+ },
+ TimingFunction::Steps(steps, pos) => {
+ Self::calculate_step_output(*steps, *pos, progress, before_flag)
+ },
+ TimingFunction::LinearFunction(function) => function.at(progress as f32).into(),
+ TimingFunction::Keyword(keyword) => match keyword {
+ TimingKeyword::Linear => progress,
+ TimingKeyword::Ease => {
+ Bezier::calculate_bezier_output(progress, epsilon, 0.25, 0.1, 0.25, 1.)
+ },
+ TimingKeyword::EaseIn => {
+ Bezier::calculate_bezier_output(progress, epsilon, 0.42, 0., 1., 1.)
+ },
+ TimingKeyword::EaseOut => {
+ Bezier::calculate_bezier_output(progress, epsilon, 0., 0., 0.58, 1.)
+ },
+ TimingKeyword::EaseInOut => {
+ Bezier::calculate_bezier_output(progress, epsilon, 0.42, 0., 0.58, 1.)
+ },
+ },
+ };
+
+ // The output progress value of an easing function is a real number in the range:
+ // [-inf, inf].
+ // https://drafts.csswg.org/css-easing-1/#output-progress-value
+ //
+ // However, we expect to use the finite progress for interpolation and web-animations
+ // https://drafts.csswg.org/css-values-4/#interpolation
+ // https://drafts.csswg.org/web-animations-1/#dom-computedeffecttiming-progress
+ //
+ // So we clamp the infinite progress, per the spec issue:
+ // https://github.com/w3c/csswg-drafts/issues/8344
+ progress.min(f64::MAX).max(f64::MIN)
+ }
+}
diff --git a/servo/components/style/values/computed/effects.rs b/servo/components/style/values/computed/effects.rs
new file mode 100644
index 0000000000..b0a92024ca
--- /dev/null
+++ b/servo/components/style/values/computed/effects.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Computed types for CSS values related to effects.
+
+use crate::values::computed::color::Color;
+use crate::values::computed::length::{Length, NonNegativeLength};
+#[cfg(feature = "gecko")]
+use crate::values::computed::url::ComputedUrl;
+use crate::values::computed::{Angle, NonNegativeNumber, ZeroToOneNumber};
+use crate::values::generics::effects::BoxShadow as GenericBoxShadow;
+use crate::values::generics::effects::Filter as GenericFilter;
+use crate::values::generics::effects::SimpleShadow as GenericSimpleShadow;
+#[cfg(not(feature = "gecko"))]
+use crate::values::Impossible;
+
+/// A computed value for a single shadow of the `box-shadow` property.
+pub type BoxShadow = GenericBoxShadow<Color, Length, NonNegativeLength, Length>;
+
+/// A computed value for a single `filter`.
+#[cfg(feature = "gecko")]
+pub type Filter = GenericFilter<
+ Angle,
+ NonNegativeNumber,
+ ZeroToOneNumber,
+ NonNegativeLength,
+ SimpleShadow,
+ ComputedUrl,
+>;
+
+/// A computed value for a single `filter`.
+#[cfg(feature = "servo")]
+pub type Filter = GenericFilter<
+ Angle,
+ NonNegativeNumber,
+ ZeroToOneNumber,
+ NonNegativeLength,
+ Impossible,
+ Impossible,
+>;
+
+/// A computed value for the `drop-shadow()` filter.
+pub type SimpleShadow = GenericSimpleShadow<Color, Length, NonNegativeLength>;
diff --git a/servo/components/style/values/computed/flex.rs b/servo/components/style/values/computed/flex.rs
new file mode 100644
index 0000000000..95c497ecf6
--- /dev/null
+++ b/servo/components/style/values/computed/flex.rs
@@ -0,0 +1,19 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Computed types for CSS values related to flexbox.
+
+use crate::values::computed::Size;
+use crate::values::generics::flex::FlexBasis as GenericFlexBasis;
+
+/// A computed value for the `flex-basis` property.
+pub type FlexBasis = GenericFlexBasis<Size>;
+
+impl FlexBasis {
+ /// `auto`
+ #[inline]
+ pub fn auto() -> Self {
+ GenericFlexBasis::Size(Size::auto())
+ }
+}
diff --git a/servo/components/style/values/computed/font.rs b/servo/components/style/values/computed/font.rs
new file mode 100644
index 0000000000..de0a5e372b
--- /dev/null
+++ b/servo/components/style/values/computed/font.rs
@@ -0,0 +1,1369 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Computed values for font properties
+
+use crate::parser::{Parse, ParserContext};
+use crate::values::animated::ToAnimatedValue;
+use crate::values::computed::{
+ Angle, Context, Integer, Length, NonNegativeLength, NonNegativeNumber, Number, Percentage,
+ ToComputedValue,
+};
+use crate::values::generics::font::{
+ FeatureTagValue, FontSettings, TaggedFontValue, VariationValue,
+};
+use crate::values::generics::{font as generics, NonNegative};
+use crate::values::resolved::{Context as ResolvedContext, ToResolvedValue};
+use crate::values::specified::font::{
+ self as specified, KeywordInfo, MAX_FONT_WEIGHT, MIN_FONT_WEIGHT,
+};
+use crate::values::specified::length::{FontBaseSize, LineHeightBase, NoCalcLength};
+use crate::Atom;
+use cssparser::{serialize_identifier, CssStringWriter, Parser};
+#[cfg(feature = "gecko")]
+use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
+use num_traits::abs;
+use num_traits::cast::AsPrimitive;
+use std::fmt::{self, Write};
+use style_traits::{CssWriter, ParseError, ToCss};
+
+pub use crate::values::computed::Length as MozScriptMinSize;
+pub use crate::values::specified::font::MozScriptSizeMultiplier;
+pub use crate::values::specified::font::{FontPalette, FontSynthesis};
+pub use crate::values::specified::font::{
+ FontVariantAlternates, FontVariantEastAsian, FontVariantLigatures, FontVariantNumeric, XLang,
+ XTextScale,
+};
+pub use crate::values::specified::Integer as SpecifiedInteger;
+pub use crate::values::specified::Number as SpecifiedNumber;
+
+/// Generic template for font property type classes that use a fixed-point
+/// internal representation with `FRACTION_BITS` for the fractional part.
+///
+/// Values are constructed from and exposed as floating-point, but stored
+/// internally as fixed point, so there will be a quantization effect on
+/// fractional values, depending on the number of fractional bits used.
+///
+/// Using (16-bit) fixed-point types rather than floats for these style
+/// attributes reduces the memory footprint of gfxFontEntry and gfxFontStyle; it
+/// will also tend to reduce the number of distinct font instances that get
+/// created, particularly when styles are animated or set to arbitrary values
+/// (e.g. by sliders in the UI), which should reduce pressure on graphics
+/// resources and improve cache hit rates.
+///
+/// cbindgen:derive-lt
+/// cbindgen:derive-lte
+/// cbindgen:derive-gt
+/// cbindgen:derive-gte
+#[repr(C)]
+#[derive(
+ Clone,
+ ComputeSquaredDistance,
+ Copy,
+ Debug,
+ Hash,
+ MallocSizeOf,
+ PartialEq,
+ PartialOrd,
+ ToResolvedValue,
+)]
+pub struct FixedPoint<T, const FRACTION_BITS: u16> {
+ /// The actual representation.
+ pub value: T,
+}
+
+impl<T, const FRACTION_BITS: u16> FixedPoint<T, FRACTION_BITS>
+where
+ T: AsPrimitive<f32>,
+ f32: AsPrimitive<T>,
+ u16: AsPrimitive<T>,
+{
+ const SCALE: u16 = 1 << FRACTION_BITS;
+ const INVERSE_SCALE: f32 = 1.0 / Self::SCALE as f32;
+
+ /// Returns a fixed-point bit from a floating-point context.
+ pub fn from_float(v: f32) -> Self {
+ Self {
+ value: (v * Self::SCALE as f32).round().as_(),
+ }
+ }
+
+ /// Returns the floating-point representation.
+ pub fn to_float(&self) -> f32 {
+ self.value.as_() * Self::INVERSE_SCALE
+ }
+}
+
+// We implement this and mul below only for u16 types, because u32 types might need more care about
+// overflow. But it's not hard to implement in either case.
+impl<const FRACTION_BITS: u16> std::ops::Div for FixedPoint<u16, FRACTION_BITS> {
+ type Output = Self;
+ fn div(self, rhs: Self) -> Self {
+ Self {
+ value: (((self.value as u32) << (FRACTION_BITS as u32)) / (rhs.value as u32)) as u16,
+ }
+ }
+}
+impl<const FRACTION_BITS: u16> std::ops::Mul for FixedPoint<u16, FRACTION_BITS> {
+ type Output = Self;
+ fn mul(self, rhs: Self) -> Self {
+ Self {
+ value: (((self.value as u32) * (rhs.value as u32)) >> (FRACTION_BITS as u32)) as u16,
+ }
+ }
+}
+
+/// font-weight: range 1..1000, fractional values permitted; keywords
+/// 'normal', 'bold' aliased to 400, 700 respectively.
+///
+/// We use an unsigned 10.6 fixed-point value (range 0.0 - 1023.984375)
+pub const FONT_WEIGHT_FRACTION_BITS: u16 = 6;
+
+/// This is an alias which is useful mostly as a cbindgen / C++ inference
+/// workaround.
+pub type FontWeightFixedPoint = FixedPoint<u16, FONT_WEIGHT_FRACTION_BITS>;
+
+/// A value for the font-weight property per:
+///
+/// https://drafts.csswg.org/css-fonts-4/#propdef-font-weight
+///
+/// cbindgen:derive-lt
+/// cbindgen:derive-lte
+/// cbindgen:derive-gt
+/// cbindgen:derive-gte
+#[derive(
+ Clone,
+ ComputeSquaredDistance,
+ Copy,
+ Debug,
+ Hash,
+ MallocSizeOf,
+ PartialEq,
+ PartialOrd,
+ ToResolvedValue,
+)]
+#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+#[repr(C)]
+pub struct FontWeight(FontWeightFixedPoint);
+impl ToAnimatedValue for FontWeight {
+ type AnimatedValue = Number;
+
+ #[inline]
+ fn to_animated_value(self) -> Self::AnimatedValue {
+ self.value()
+ }
+
+ #[inline]
+ fn from_animated_value(animated: Self::AnimatedValue) -> Self {
+ FontWeight::from_float(animated)
+ }
+}
+
+impl ToCss for FontWeight {
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: fmt::Write,
+ {
+ self.value().to_css(dest)
+ }
+}
+
+impl FontWeight {
+ /// The `normal` keyword.
+ pub const NORMAL: FontWeight = FontWeight(FontWeightFixedPoint {
+ value: 400 << FONT_WEIGHT_FRACTION_BITS,
+ });
+
+ /// The `bold` value.
+ pub const BOLD: FontWeight = FontWeight(FontWeightFixedPoint {
+ value: 700 << FONT_WEIGHT_FRACTION_BITS,
+ });
+
+ /// The threshold from which we consider a font bold.
+ pub const BOLD_THRESHOLD: FontWeight = FontWeight(FontWeightFixedPoint {
+ value: 600 << FONT_WEIGHT_FRACTION_BITS,
+ });
+
+ /// Returns the `normal` keyword value.
+ pub fn normal() -> Self {
+ Self::NORMAL
+ }
+
+ /// Weither this weight is bold
+ pub fn is_bold(&self) -> bool {
+ *self >= Self::BOLD_THRESHOLD
+ }
+
+ /// Returns the value as a float.
+ pub fn value(&self) -> f32 {
+ self.0.to_float()
+ }
+
+ /// Construct a valid weight from a float value.
+ pub fn from_float(v: f32) -> Self {
+ Self(FixedPoint::from_float(
+ v.max(MIN_FONT_WEIGHT).min(MAX_FONT_WEIGHT),
+ ))
+ }
+
+ /// Return the bolder weight.
+ ///
+ /// See the table in:
+ /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
+ pub fn bolder(self) -> Self {
+ let value = self.value();
+ if value < 350. {
+ return Self::NORMAL;
+ }
+ if value < 550. {
+ return Self::BOLD;
+ }
+ Self::from_float(value.max(900.))
+ }
+
+ /// Return the lighter weight.
+ ///
+ /// See the table in:
+ /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
+ pub fn lighter(self) -> Self {
+ let value = self.value();
+ if value < 550. {
+ return Self::from_float(value.min(100.));
+ }
+ if value < 750. {
+ return Self::NORMAL;
+ }
+ Self::BOLD
+ }
+}
+
+#[derive(
+ Animate,
+ Clone,
+ ComputeSquaredDistance,
+ Copy,
+ Debug,
+ MallocSizeOf,
+ PartialEq,
+ ToAnimatedZero,
+ ToCss,
+ ToResolvedValue,
+)]
+#[cfg_attr(feature = "servo", derive(Serialize, Deserialize))]
+/// The computed value of font-size
+pub struct FontSize {
+ /// The computed size, that we use to compute ems etc. This accounts for
+ /// e.g., text-zoom.
+ pub computed_size: NonNegativeLength,
+ /// The actual used size. This is the computed font size, potentially
+ /// constrained by other factors like minimum font-size settings and so on.
+ #[css(skip)]
+ pub used_size: NonNegativeLength,
+ /// If derived from a keyword, the keyword and additional transformations applied to it
+ #[css(skip)]
+ pub keyword_info: KeywordInfo,
+}
+
+impl FontSize {
+ /// The actual computed font size.
+ #[inline]
+ pub fn computed_size(&self) -> Length {
+ self.computed_size.0
+ }
+
+ /// The actual used font size.
+ #[inline]
+ pub fn used_size(&self) -> Length {
+ self.used_size.0
+ }
+
+ #[inline]
+ /// Get default value of font size.
+ pub fn medium() -> Self {
+ Self {
+ computed_size: NonNegative(Length::new(specified::FONT_MEDIUM_PX)),
+ used_size: NonNegative(Length::new(specified::FONT_MEDIUM_PX)),
+ keyword_info: KeywordInfo::medium(),
+ }
+ }
+}
+
+impl ToAnimatedValue for FontSize {
+ type AnimatedValue = Length;
+
+ #[inline]
+ fn to_animated_value(self) -> Self::AnimatedValue {
+ self.computed_size.0
+ }
+
+ #[inline]
+ fn from_animated_value(animated: Self::AnimatedValue) -> Self {
+ FontSize {
+ computed_size: NonNegative(animated.clamp_to_non_negative()),
+ used_size: NonNegative(animated.clamp_to_non_negative()),
+ keyword_info: KeywordInfo::none(),
+ }
+ }
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToResolvedValue)]
+#[cfg_attr(feature = "servo", derive(Hash, MallocSizeOf, Serialize, Deserialize))]
+/// Specifies a prioritized list of font family names or generic family names.
+#[repr(C)]
+pub struct FontFamily {
+ /// The actual list of family names.
+ pub families: FontFamilyList,
+ /// Whether this font-family came from a specified system-font.
+ pub is_system_font: bool,
+ /// Whether this is the initial font-family that might react to language
+ /// changes.
+ pub is_initial: bool,
+}
+
+macro_rules! static_font_family {
+ ($ident:ident, $family:expr) => {
+ lazy_static! {
+ static ref $ident: FontFamily = FontFamily {
+ families: FontFamilyList {
+ list: crate::ArcSlice::from_iter_leaked(std::iter::once($family)),
+ },
+ is_system_font: false,
+ is_initial: false,
+ };
+ }
+ };
+}
+
+impl FontFamily {
+ #[inline]
+ /// Get default font family as `serif` which is a generic font-family
+ pub fn serif() -> Self {
+ Self::generic(GenericFontFamily::Serif).clone()
+ }
+
+ /// Returns the font family for `-moz-bullet-font`.
+ pub(crate) fn moz_bullet() -> &'static Self {
+ static_font_family!(
+ MOZ_BULLET,
+ SingleFontFamily::FamilyName(FamilyName {
+ name: atom!("-moz-bullet-font"),
+ syntax: FontFamilyNameSyntax::Identifiers,
+ })
+ );
+
+ &*MOZ_BULLET
+ }
+
+ /// Returns a font family for a single system font.
+ pub fn for_system_font(name: &str) -> Self {
+ Self {
+ families: FontFamilyList {
+ list: crate::ArcSlice::from_iter(std::iter::once(SingleFontFamily::FamilyName(
+ FamilyName {
+ name: Atom::from(name),
+ syntax: FontFamilyNameSyntax::Identifiers,
+ },
+ ))),
+ },
+ is_system_font: true,
+ is_initial: false,
+ }
+ }
+
+ /// Returns a generic font family.
+ pub fn generic(generic: GenericFontFamily) -> &'static Self {
+ macro_rules! generic_font_family {
+ ($ident:ident, $family:ident) => {
+ static_font_family!(
+ $ident,
+ SingleFontFamily::Generic(GenericFontFamily::$family)
+ )
+ };
+ }
+
+ generic_font_family!(SERIF, Serif);
+ generic_font_family!(SANS_SERIF, SansSerif);
+ generic_font_family!(MONOSPACE, Monospace);
+ generic_font_family!(CURSIVE, Cursive);
+ generic_font_family!(FANTASY, Fantasy);
+ generic_font_family!(MOZ_EMOJI, MozEmoji);
+ generic_font_family!(SYSTEM_UI, SystemUi);
+
+ let family = match generic {
+ GenericFontFamily::None => {
+ debug_assert!(false, "Bogus caller!");
+ &*SERIF
+ },
+ GenericFontFamily::Serif => &*SERIF,
+ GenericFontFamily::SansSerif => &*SANS_SERIF,
+ GenericFontFamily::Monospace => &*MONOSPACE,
+ GenericFontFamily::Cursive => &*CURSIVE,
+ GenericFontFamily::Fantasy => &*FANTASY,
+ GenericFontFamily::MozEmoji => &*MOZ_EMOJI,
+ GenericFontFamily::SystemUi => &*SYSTEM_UI,
+ };
+ debug_assert_eq!(
+ *family.families.iter().next().unwrap(),
+ SingleFontFamily::Generic(generic)
+ );
+ family
+ }
+}
+
+#[cfg(feature = "gecko")]
+impl MallocSizeOf for FontFamily {
+ fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
+ use malloc_size_of::MallocUnconditionalSizeOf;
+ // SharedFontList objects are generally measured from the pointer stored
+ // in the specified value. So only count this if the SharedFontList is
+ // unshared.
+ let shared_font_list = &self.families.list;
+ if shared_font_list.is_unique() {
+ shared_font_list.unconditional_size_of(ops)
+ } else {
+ 0
+ }
+ }
+}
+
+impl ToCss for FontFamily {
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: fmt::Write,
+ {
+ let mut iter = self.families.iter();
+ match iter.next() {
+ Some(f) => f.to_css(dest)?,
+ None => return Ok(()),
+ }
+ for family in iter {
+ dest.write_str(", ")?;
+ family.to_css(dest)?;
+ }
+ Ok(())
+ }
+}
+
+/// The name of a font family of choice.
+#[derive(
+ Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
+)]
+#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+#[repr(C)]
+pub struct FamilyName {
+ /// Name of the font family.
+ pub name: Atom,
+ /// Syntax of the font family.
+ pub syntax: FontFamilyNameSyntax,
+}
+
+impl FamilyName {
+ fn is_known_icon_font_family(&self) -> bool {
+ use crate::gecko_bindings::bindings;
+ unsafe { bindings::Gecko_IsKnownIconFontFamily(self.name.as_ptr()) }
+ }
+}
+
+impl ToCss for FamilyName {
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: fmt::Write,
+ {
+ match self.syntax {
+ FontFamilyNameSyntax::Quoted => {
+ dest.write_char('"')?;
+ write!(CssStringWriter::new(dest), "{}", self.name)?;
+ dest.write_char('"')
+ },
+ FontFamilyNameSyntax::Identifiers => {
+ let mut first = true;
+ for ident in self.name.to_string().split(' ') {
+ if first {
+ first = false;
+ } else {
+ dest.write_char(' ')?;
+ }
+ debug_assert!(
+ !ident.is_empty(),
+ "Family name with leading, \
+ trailing, or consecutive white spaces should \
+ have been marked quoted by the parser"
+ );
+ serialize_identifier(ident, dest)?;
+ }
+ Ok(())
+ },
+ }
+ }
+}
+
+#[derive(
+ Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
+)]
+#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+/// Font family names must either be given quoted as strings,
+/// or unquoted as a sequence of one or more identifiers.
+#[repr(u8)]
+pub enum FontFamilyNameSyntax {
+ /// The family name was specified in a quoted form, e.g. "Font Name"
+ /// or 'Font Name'.
+ Quoted,
+
+ /// The family name was specified in an unquoted form as a sequence of
+ /// identifiers.
+ Identifiers,
+}
+
+/// A set of faces that vary in weight, width or slope.
+/// cbindgen:derive-mut-casts=true
+#[derive(
+ Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToComputedValue, ToResolvedValue, ToShmem,
+)]
+#[cfg_attr(feature = "servo", derive(Deserialize, Serialize, Hash))]
+#[repr(u8)]
+pub enum SingleFontFamily {
+ /// The name of a font family of choice.
+ FamilyName(FamilyName),
+ /// Generic family name.
+ Generic(GenericFontFamily),
+}
+
+fn system_ui_enabled(_: &ParserContext) -> bool {
+ static_prefs::pref!("layout.css.system-ui.enabled")
+}
+
+/// A generic font-family name.
+///
+/// The order here is important, if you change it make sure that
+/// `gfxPlatformFontList.h`s ranged array and `gfxFontFamilyList`'s
+/// sSingleGenerics are updated as well.
+///
+/// NOTE(emilio): Should be u8, but it's a u32 because of ABI issues between GCC
+/// and LLVM see https://bugs.llvm.org/show_bug.cgi?id=44228 / bug 1600735 /
+/// bug 1726515.
+#[derive(
+ Clone,
+ Copy,
+ Debug,
+ Eq,
+ Hash,
+ MallocSizeOf,
+ PartialEq,
+ Parse,
+ ToCss,
+ ToComputedValue,
+ ToResolvedValue,
+ ToShmem,
+)]
+#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+#[repr(u32)]
+#[allow(missing_docs)]
+pub enum GenericFontFamily {
+ /// No generic family specified, only for internal usage.
+ ///
+ /// NOTE(emilio): Gecko code relies on this variant being zero.
+ #[css(skip)]
+ None = 0,
+ Serif,
+ SansSerif,
+ #[parse(aliases = "-moz-fixed")]
+ Monospace,
+ Cursive,
+ Fantasy,
+ #[parse(condition = "system_ui_enabled")]
+ SystemUi,
+ /// An internal value for emoji font selection.
+ #[css(skip)]
+ #[cfg(feature = "gecko")]
+ MozEmoji,
+}
+
+impl GenericFontFamily {
+ /// When we disallow websites to override fonts, we ignore some generic
+ /// families that the website might specify, since they're not configured by
+ /// the user. See bug 789788 and bug 1730098.
+ pub(crate) fn valid_for_user_font_prioritization(self) -> bool {
+ match self {
+ Self::None | Self::Fantasy | Self::Cursive | Self::SystemUi | Self::MozEmoji => false,
+
+ Self::Serif | Self::SansSerif | Self::Monospace => true,
+ }
+ }
+}
+
+impl Parse for SingleFontFamily {
+ /// Parse a font-family value.
+ fn parse<'i, 't>(
+ context: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ ) -> Result<Self, ParseError<'i>> {
+ if let Ok(value) = input.try_parse(|i| i.expect_string_cloned()) {
+ return Ok(SingleFontFamily::FamilyName(FamilyName {
+ name: Atom::from(&*value),
+ syntax: FontFamilyNameSyntax::Quoted,
+ }));
+ }
+
+ if let Ok(generic) = input.try_parse(|i| GenericFontFamily::parse(context, i)) {
+ return Ok(SingleFontFamily::Generic(generic));
+ }
+
+ let first_ident = input.expect_ident_cloned()?;
+ let reserved = match_ignore_ascii_case! { &first_ident,
+ // https://drafts.csswg.org/css-fonts/#propdef-font-family
+ // "Font family names that happen to be the same as a keyword value
+ // (`inherit`, `serif`, `sans-serif`, `monospace`, `fantasy`, and `cursive`)
+ // must be quoted to prevent confusion with the keywords with the same names.
+ // The keywords ‘initial’ and ‘default’ are reserved for future use
+ // and must also be quoted when used as font names.
+ // UAs must not consider these keywords as matching the <family-name> type."
+ "inherit" | "initial" | "unset" | "revert" | "default" => true,
+ _ => false,
+ };
+
+ let mut value = first_ident.as_ref().to_owned();
+ let mut serialize_quoted = value.contains(' ');
+
+ // These keywords are not allowed by themselves.
+ // The only way this value can be valid with with another keyword.
+ if reserved {
+ let ident = input.expect_ident()?;
+ serialize_quoted = serialize_quoted || ident.contains(' ');
+ value.push(' ');
+ value.push_str(&ident);
+ }
+ while let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) {
+ serialize_quoted = serialize_quoted || ident.contains(' ');
+ value.push(' ');
+ value.push_str(&ident);
+ }
+ let syntax = if serialize_quoted {
+ // For font family names which contains special white spaces, e.g.
+ // `font-family: \ a\ \ b\ \ c\ ;`, it is tricky to serialize them
+ // as identifiers correctly. Just mark them quoted so we don't need
+ // to worry about them in serialization code.
+ FontFamilyNameSyntax::Quoted
+ } else {
+ FontFamilyNameSyntax::Identifiers
+ };
+ Ok(SingleFontFamily::FamilyName(FamilyName {
+ name: Atom::from(value),
+ syntax,
+ }))
+ }
+}
+
+#[cfg(feature = "servo")]
+impl SingleFontFamily {
+ /// Get the corresponding font-family with Atom
+ pub fn from_atom(input: Atom) -> SingleFontFamily {
+ match input {
+ atom!("serif") => return SingleFontFamily::Generic(GenericFontFamily::Serif),
+ atom!("sans-serif") => return SingleFontFamily::Generic(GenericFontFamily::SansSerif),
+ atom!("cursive") => return SingleFontFamily::Generic(GenericFontFamily::Cursive),
+ atom!("fantasy") => return SingleFontFamily::Generic(GenericFontFamily::Fantasy),
+ atom!("monospace") => return SingleFontFamily::Generic(GenericFontFamily::Monospace),
+ _ => {},
+ }
+
+ match_ignore_ascii_case! { &input,
+ "serif" => return SingleFontFamily::Generic(GenericFontFamily::Serif),
+ "sans-serif" => return SingleFontFamily::Generic(GenericFontFamily::SansSerif),
+ "cursive" => return SingleFontFamily::Generic(GenericFontFamily::Cursive),
+ "fantasy" => return SingleFontFamily::Generic(GenericFontFamily::Fantasy),
+ "monospace" => return SingleFontFamily::Generic(GenericFontFamily::Monospace),
+ _ => {}
+ }
+
+ // We don't know if it's quoted or not. So we set it to
+ // quoted by default.
+ SingleFontFamily::FamilyName(FamilyName {
+ name: input,
+ syntax: FontFamilyNameSyntax::Quoted,
+ })
+ }
+}
+
+/// A list of font families.
+#[derive(Clone, Debug, ToComputedValue, ToResolvedValue, ToShmem, PartialEq, Eq)]
+#[repr(C)]
+pub struct FontFamilyList {
+ /// The actual list of font families specified.
+ pub list: crate::ArcSlice<SingleFontFamily>,
+}
+
+impl FontFamilyList {
+ /// Return iterator of SingleFontFamily
+ pub fn iter(&self) -> impl Iterator<Item = &SingleFontFamily> {
+ self.list.iter()
+ }
+
+ /// If there's a generic font family on the list which is suitable for user
+ /// font prioritization, then move it ahead of the other families in the list,
+ /// except for any families known to be ligature-based icon fonts, where using a
+ /// generic instead of the site's specified font may cause substantial breakage.
+ /// If no suitable generic is found in the list, insert the default generic ahead
+ /// of all the listed families except for known ligature-based icon fonts.
+ pub(crate) fn prioritize_first_generic_or_prepend(&mut self, generic: GenericFontFamily) {
+ let mut index_of_first_generic = None;
+ let mut target_index = None;
+
+ for (i, f) in self.iter().enumerate() {
+ match &*f {
+ SingleFontFamily::Generic(f) => {
+ if index_of_first_generic.is_none() && f.valid_for_user_font_prioritization() {
+ // If we haven't found a target position, there's nothing to do;
+ // this entry is already ahead of everything except any whitelisted
+ // icon fonts.
+ if target_index.is_none() {
+ return;
+ }
+ index_of_first_generic = Some(i);
+ break;
+ }
+ // A non-prioritized generic (e.g. cursive, fantasy) becomes the target
+ // position for prioritization, just like arbitrary named families.
+ if target_index.is_none() {
+ target_index = Some(i);
+ }
+ },
+ SingleFontFamily::FamilyName(fam) => {
+ // Target position for the first generic is in front of the first
+ // non-whitelisted icon font family we find.
+ if target_index.is_none() && !fam.is_known_icon_font_family() {
+ target_index = Some(i);
+ }
+ },
+ }
+ }
+
+ let mut new_list = self.list.iter().cloned().collect::<Vec<_>>();
+ let first_generic = match index_of_first_generic {
+ Some(i) => new_list.remove(i),
+ None => SingleFontFamily::Generic(generic),
+ };
+
+ if let Some(i) = target_index {
+ new_list.insert(i, first_generic);
+ } else {
+ new_list.push(first_generic);
+ }
+ self.list = crate::ArcSlice::from_iter(new_list.into_iter());
+ }
+
+ /// Returns whether we need to prioritize user fonts.
+ pub(crate) fn needs_user_font_prioritization(&self) -> bool {
+ self.iter().next().map_or(true, |f| match f {
+ SingleFontFamily::Generic(f) => !f.valid_for_user_font_prioritization(),
+ _ => true,
+ })
+ }
+
+ /// Return the generic ID if it is a single generic font
+ pub fn single_generic(&self) -> Option<GenericFontFamily> {
+ let mut iter = self.iter();
+ if let Some(SingleFontFamily::Generic(f)) = iter.next() {
+ if iter.next().is_none() {
+ return Some(*f);
+ }
+ }
+ None
+ }
+}
+
+/// Preserve the readability of text when font fallback occurs.
+pub type FontSizeAdjust = generics::GenericFontSizeAdjust<NonNegativeNumber>;
+
+impl FontSizeAdjust {
+ #[inline]
+ /// Default value of font-size-adjust
+ pub fn none() -> Self {
+ FontSizeAdjust::None
+ }
+}
+
+impl ToComputedValue for specified::FontSizeAdjust {
+ type ComputedValue = FontSizeAdjust;
+
+ fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+ use crate::font_metrics::FontMetricsOrientation;
+
+ let font_metrics = |vertical| {
+ let orient = if vertical {
+ FontMetricsOrientation::MatchContextPreferVertical
+ } else {
+ FontMetricsOrientation::Horizontal
+ };
+ let metrics = context.query_font_metrics(FontBaseSize::CurrentStyle, orient, false);
+ let font_size = context.style().get_font().clone_font_size().used_size.0;
+ (metrics, font_size)
+ };
+
+ // Macro to resolve a from-font value using the given metric field. If not present,
+ // returns the fallback value, or if that is negative, resolves using ascent instead
+ // of the missing field (this is the fallback for cap-height).
+ macro_rules! resolve {
+ ($basis:ident, $value:expr, $vertical:expr, $field:ident, $fallback:expr) => {{
+ match $value {
+ specified::FontSizeAdjustFactor::Number(f) => {
+ FontSizeAdjust::$basis(f.to_computed_value(context))
+ },
+ specified::FontSizeAdjustFactor::FromFont => {
+ let (metrics, font_size) = font_metrics($vertical);
+ let ratio = if let Some(metric) = metrics.$field {
+ metric / font_size
+ } else if $fallback >= 0.0 {
+ $fallback
+ } else {
+ metrics.ascent / font_size
+ };
+ if ratio.is_nan() {
+ FontSizeAdjust::$basis(NonNegative(abs($fallback)))
+ } else {
+ FontSizeAdjust::$basis(NonNegative(ratio))
+ }
+ },
+ }
+ }};
+ }
+
+ match *self {
+ Self::None => FontSizeAdjust::None,
+ Self::ExHeight(val) => resolve!(ExHeight, val, false, x_height, 0.5),
+ Self::CapHeight(val) => {
+ resolve!(CapHeight, val, false, cap_height, -1.0 /* fall back to ascent */)
+ },
+ Self::ChWidth(val) => resolve!(ChWidth, val, false, zero_advance_measure, 0.5),
+ Self::IcWidth(val) => resolve!(IcWidth, val, false, ic_width, 1.0),
+ Self::IcHeight(val) => resolve!(IcHeight, val, true, ic_width, 1.0),
+ }
+ }
+
+ fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+ macro_rules! case {
+ ($basis:ident, $val:expr) => {
+ Self::$basis(specified::FontSizeAdjustFactor::Number(
+ ToComputedValue::from_computed_value($val),
+ ))
+ };
+ }
+ match *computed {
+ FontSizeAdjust::None => Self::None,
+ FontSizeAdjust::ExHeight(ref val) => case!(ExHeight, val),
+ FontSizeAdjust::CapHeight(ref val) => case!(CapHeight, val),
+ FontSizeAdjust::ChWidth(ref val) => case!(ChWidth, val),
+ FontSizeAdjust::IcWidth(ref val) => case!(IcWidth, val),
+ FontSizeAdjust::IcHeight(ref val) => case!(IcHeight, val),
+ }
+ }
+}
+
+/// Use FontSettings as computed type of FontFeatureSettings.
+pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
+
+/// The computed value for font-variation-settings.
+pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
+
+// The computed value of font-{feature,variation}-settings discards values
+// with duplicate tags, keeping only the last occurrence of each tag.
+fn dedup_font_settings<T>(settings_list: &mut Vec<T>)
+where
+ T: TaggedFontValue,
+{
+ if settings_list.len() > 1 {
+ settings_list.sort_by_key(|k| k.tag().0);
+ // dedup() keeps the first of any duplicates, but we want the last,
+ // so we implement it manually here.
+ let mut prev_tag = settings_list.last().unwrap().tag();
+ for i in (0..settings_list.len() - 1).rev() {
+ let cur_tag = settings_list[i].tag();
+ if cur_tag == prev_tag {
+ settings_list.remove(i);
+ }
+ prev_tag = cur_tag;
+ }
+ }
+}
+
+impl<T> ToComputedValue for FontSettings<T>
+where
+ T: ToComputedValue,
+ <T as ToComputedValue>::ComputedValue: TaggedFontValue,
+{
+ type ComputedValue = FontSettings<T::ComputedValue>;
+
+ fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+ let mut v = self
+ .0
+ .iter()
+ .map(|item| item.to_computed_value(context))
+ .collect::<Vec<_>>();
+ dedup_font_settings(&mut v);
+ FontSettings(v.into_boxed_slice())
+ }
+
+ fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+ Self(
+ computed
+ .0
+ .iter()
+ .map(T::from_computed_value)
+ .collect::<Vec<_>>()
+ .into_boxed_slice(),
+ )
+ }
+}
+
+/// font-language-override can only have a single 1-4 ASCII character
+/// OpenType "language system" tag, so we should be able to compute
+/// it and store it as a 32-bit integer
+/// (see http://www.microsoft.com/typography/otspec/languagetags.htm).
+#[derive(
+ Clone,
+ Copy,
+ Debug,
+ Eq,
+ MallocSizeOf,
+ PartialEq,
+ SpecifiedValueInfo,
+ ToComputedValue,
+ ToResolvedValue,
+ ToShmem,
+)]
+#[repr(C)]
+#[value_info(other_values = "normal")]
+pub struct FontLanguageOverride(pub u32);
+
+impl FontLanguageOverride {
+ #[inline]
+ /// Get computed default value of `font-language-override` with 0
+ pub fn normal() -> FontLanguageOverride {
+ FontLanguageOverride(0)
+ }
+
+ /// Returns this value as a `&str`, backed by `storage`.
+ #[inline]
+ pub(crate) fn to_str(self, storage: &mut [u8; 4]) -> &str {
+ *storage = u32::to_be_bytes(self.0);
+ // Safe because we ensure it's ASCII during parsing
+ let slice = if cfg!(debug_assertions) {
+ std::str::from_utf8(&storage[..]).unwrap()
+ } else {
+ unsafe { std::str::from_utf8_unchecked(&storage[..]) }
+ };
+ slice.trim_end()
+ }
+
+ /// Unsafe because `Self::to_str` requires the value to represent a UTF-8
+ /// string.
+ #[inline]
+ pub unsafe fn from_u32(value: u32) -> Self {
+ Self(value)
+ }
+}
+
+impl ToCss for FontLanguageOverride {
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: fmt::Write,
+ {
+ if self.0 == 0 {
+ return dest.write_str("normal");
+ }
+ self.to_str(&mut [0; 4]).to_css(dest)
+ }
+}
+
+// FIXME(emilio): Make Gecko use the cbindgen'd fontLanguageOverride, then
+// remove this.
+#[cfg(feature = "gecko")]
+impl From<u32> for FontLanguageOverride {
+ fn from(v: u32) -> Self {
+ unsafe { Self::from_u32(v) }
+ }
+}
+
+#[cfg(feature = "gecko")]
+impl From<FontLanguageOverride> for u32 {
+ fn from(v: FontLanguageOverride) -> u32 {
+ v.0
+ }
+}
+
+impl ToComputedValue for specified::MozScriptMinSize {
+ type ComputedValue = MozScriptMinSize;
+
+ fn to_computed_value(&self, cx: &Context) -> MozScriptMinSize {
+ // this value is used in the computation of font-size, so
+ // we use the parent size
+ let base_size = FontBaseSize::InheritedStyle;
+ let line_height_base = LineHeightBase::InheritedStyle;
+ match self.0 {
+ NoCalcLength::FontRelative(value) => {
+ value.to_computed_value(cx, base_size, line_height_base)
+ },
+ NoCalcLength::ServoCharacterWidth(value) => {
+ value.to_computed_value(base_size.resolve(cx).computed_size())
+ },
+ ref l => l.to_computed_value(cx),
+ }
+ }
+
+ fn from_computed_value(other: &MozScriptMinSize) -> Self {
+ specified::MozScriptMinSize(ToComputedValue::from_computed_value(other))
+ }
+}
+
+/// The computed value of the math-depth property.
+pub type MathDepth = i8;
+
+#[cfg(feature = "gecko")]
+impl ToComputedValue for specified::MathDepth {
+ type ComputedValue = MathDepth;
+
+ fn to_computed_value(&self, cx: &Context) -> i8 {
+ use crate::properties::longhands::math_style::SpecifiedValue as MathStyleValue;
+ use std::{cmp, i8};
+
+ let int = match *self {
+ specified::MathDepth::AutoAdd => {
+ let parent = cx.builder.get_parent_font().clone_math_depth() as i32;
+ let style = cx.builder.get_parent_font().clone_math_style();
+ if style == MathStyleValue::Compact {
+ parent.saturating_add(1)
+ } else {
+ parent
+ }
+ },
+ specified::MathDepth::Add(rel) => {
+ let parent = cx.builder.get_parent_font().clone_math_depth();
+ (parent as i32).saturating_add(rel.to_computed_value(cx))
+ },
+ specified::MathDepth::Absolute(abs) => abs.to_computed_value(cx),
+ };
+ cmp::min(int, i8::MAX as i32) as i8
+ }
+
+ fn from_computed_value(other: &i8) -> Self {
+ let computed_value = *other as i32;
+ specified::MathDepth::Absolute(SpecifiedInteger::from_computed_value(&computed_value))
+ }
+}
+
+/// - Use a signed 8.8 fixed-point value (representable range -128.0..128)
+///
+/// Values of <angle> below -90 or above 90 not permitted, so we use out of
+/// range values to represent normal | oblique
+pub const FONT_STYLE_FRACTION_BITS: u16 = 8;
+
+/// This is an alias which is useful mostly as a cbindgen / C++ inference
+/// workaround.
+pub type FontStyleFixedPoint = FixedPoint<i16, FONT_STYLE_FRACTION_BITS>;
+
+/// The computed value of `font-style`.
+///
+/// - Define out of range values min value (-128.0) as meaning 'normal'
+/// - Define max value (127.99609375) as 'italic'
+/// - Other values represent 'oblique <angle>'
+/// - Note that 'oblique 0deg' is distinct from 'normal' (should it be?)
+///
+/// cbindgen:derive-lt
+/// cbindgen:derive-lte
+/// cbindgen:derive-gt
+/// cbindgen:derive-gte
+#[derive(
+ Clone,
+ ComputeSquaredDistance,
+ Copy,
+ Debug,
+ Hash,
+ MallocSizeOf,
+ PartialEq,
+ PartialOrd,
+ ToResolvedValue,
+)]
+#[repr(C)]
+pub struct FontStyle(FontStyleFixedPoint);
+
+impl FontStyle {
+ /// The normal keyword.
+ pub const NORMAL: FontStyle = FontStyle(FontStyleFixedPoint {
+ value: 100 << FONT_STYLE_FRACTION_BITS,
+ });
+ /// The italic keyword.
+ pub const ITALIC: FontStyle = FontStyle(FontStyleFixedPoint {
+ value: 101 << FONT_STYLE_FRACTION_BITS,
+ });
+
+ /// The default angle for `font-style: oblique`.
+ /// See also https://github.com/w3c/csswg-drafts/issues/2295
+ pub const DEFAULT_OBLIQUE_DEGREES: i16 = 14;
+
+ /// The `oblique` keyword with the default degrees.
+ pub const OBLIQUE: FontStyle = FontStyle(FontStyleFixedPoint {
+ value: Self::DEFAULT_OBLIQUE_DEGREES << FONT_STYLE_FRACTION_BITS,
+ });
+
+ /// The `normal` value.
+ #[inline]
+ pub fn normal() -> Self {
+ Self::NORMAL
+ }
+
+ /// Returns the oblique angle for this style.
+ pub fn oblique(degrees: f32) -> Self {
+ Self(FixedPoint::from_float(
+ degrees
+ .max(specified::FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES)
+ .min(specified::FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES),
+ ))
+ }
+
+ /// Returns the oblique angle for this style.
+ pub fn oblique_degrees(&self) -> f32 {
+ debug_assert_ne!(*self, Self::NORMAL);
+ debug_assert_ne!(*self, Self::ITALIC);
+ self.0.to_float()
+ }
+}
+
+impl ToCss for FontStyle {
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: fmt::Write,
+ {
+ if *self == Self::NORMAL {
+ return dest.write_str("normal");
+ }
+ if *self == Self::ITALIC {
+ return dest.write_str("italic");
+ }
+ if *self == Self::OBLIQUE {
+ return dest.write_str("oblique");
+ }
+ dest.write_str("oblique ")?;
+ let angle = Angle::from_degrees(self.oblique_degrees());
+ angle.to_css(dest)?;
+ Ok(())
+ }
+}
+
+impl ToAnimatedValue for FontStyle {
+ type AnimatedValue = generics::FontStyle<Angle>;
+
+ #[inline]
+ fn to_animated_value(self) -> Self::AnimatedValue {
+ if self == Self::NORMAL {
+ // This allows us to animate between normal and oblique values. Per spec,
+ // https://drafts.csswg.org/css-fonts-4/#font-style-prop:
+ // Animation type: by computed value type; 'normal' animates as 'oblique 0deg'
+ return generics::FontStyle::Oblique(Angle::from_degrees(0.0));
+ }
+ if self == Self::ITALIC {
+ return generics::FontStyle::Italic;
+ }
+ generics::FontStyle::Oblique(Angle::from_degrees(self.oblique_degrees()))
+ }
+
+ #[inline]
+ fn from_animated_value(animated: Self::AnimatedValue) -> Self {
+ match animated {
+ generics::FontStyle::Normal => Self::NORMAL,
+ generics::FontStyle::Italic => Self::ITALIC,
+ generics::FontStyle::Oblique(ref angle) => {
+ if angle.degrees() == 0.0 {
+ // Reverse the conversion done in to_animated_value()
+ Self::NORMAL
+ } else {
+ Self::oblique(angle.degrees())
+ }
+ },
+ }
+ }
+}
+
+/// font-stretch is a percentage relative to normal.
+///
+/// We use an unsigned 10.6 fixed-point value (range 0.0 - 1023.984375)
+///
+/// We arbitrarily limit here to 1000%. (If that becomes a problem, we could
+/// reduce the number of fractional bits and increase the limit.)
+pub const FONT_STRETCH_FRACTION_BITS: u16 = 6;
+
+/// This is an alias which is useful mostly as a cbindgen / C++ inference
+/// workaround.
+pub type FontStretchFixedPoint = FixedPoint<u16, FONT_STRETCH_FRACTION_BITS>;
+
+/// A value for the font-stretch property per:
+///
+/// https://drafts.csswg.org/css-fonts-4/#propdef-font-stretch
+///
+/// cbindgen:derive-lt
+/// cbindgen:derive-lte
+/// cbindgen:derive-gt
+/// cbindgen:derive-gte
+#[derive(
+ Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToResolvedValue,
+)]
+#[repr(C)]
+pub struct FontStretch(pub FontStretchFixedPoint);
+
+impl FontStretch {
+ /// The fraction bits, as an easy-to-access-constant.
+ pub const FRACTION_BITS: u16 = FONT_STRETCH_FRACTION_BITS;
+ /// 0.5 in our floating point representation.
+ pub const HALF: u16 = 1 << (Self::FRACTION_BITS - 1);
+
+ /// The `ultra-condensed` keyword.
+ pub const ULTRA_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
+ value: 50 << Self::FRACTION_BITS,
+ });
+ /// The `extra-condensed` keyword.
+ pub const EXTRA_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
+ value: (62 << Self::FRACTION_BITS) + Self::HALF,
+ });
+ /// The `condensed` keyword.
+ pub const CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
+ value: 75 << Self::FRACTION_BITS,
+ });
+ /// The `semi-condensed` keyword.
+ pub const SEMI_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
+ value: (87 << Self::FRACTION_BITS) + Self::HALF,
+ });
+ /// The `normal` keyword.
+ pub const NORMAL: FontStretch = FontStretch(FontStretchFixedPoint {
+ value: 100 << Self::FRACTION_BITS,
+ });
+ /// The `semi-expanded` keyword.
+ pub const SEMI_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
+ value: (112 << Self::FRACTION_BITS) + Self::HALF,
+ });
+ /// The `expanded` keyword.
+ pub const EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
+ value: 125 << Self::FRACTION_BITS,
+ });
+ /// The `extra-expanded` keyword.
+ pub const EXTRA_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
+ value: 150 << Self::FRACTION_BITS,
+ });
+ /// The `ultra-expanded` keyword.
+ pub const ULTRA_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
+ value: 200 << Self::FRACTION_BITS,
+ });
+
+ /// 100%
+ pub fn hundred() -> Self {
+ Self::NORMAL
+ }
+
+ /// Converts to a computed percentage.
+ #[inline]
+ pub fn to_percentage(&self) -> Percentage {
+ Percentage(self.0.to_float() / 100.0)
+ }
+
+ /// Converts from a computed percentage value.
+ pub fn from_percentage(p: f32) -> Self {
+ Self(FixedPoint::from_float((p * 100.).max(0.0).min(1000.0)))
+ }
+
+ /// Returns a relevant stretch value from a keyword.
+ /// https://drafts.csswg.org/css-fonts-4/#font-stretch-prop
+ pub fn from_keyword(kw: specified::FontStretchKeyword) -> Self {
+ use specified::FontStretchKeyword::*;
+ match kw {
+ UltraCondensed => Self::ULTRA_CONDENSED,
+ ExtraCondensed => Self::EXTRA_CONDENSED,
+ Condensed => Self::CONDENSED,
+ SemiCondensed => Self::SEMI_CONDENSED,
+ Normal => Self::NORMAL,
+ SemiExpanded => Self::SEMI_EXPANDED,
+ Expanded => Self::EXPANDED,
+ ExtraExpanded => Self::EXTRA_EXPANDED,
+ UltraExpanded => Self::ULTRA_EXPANDED,
+ }
+ }
+
+ /// Returns the stretch keyword if we map to one of the relevant values.
+ pub fn as_keyword(&self) -> Option<specified::FontStretchKeyword> {
+ use specified::FontStretchKeyword::*;
+ // TODO: Can we use match here?
+ if *self == Self::ULTRA_CONDENSED {
+ return Some(UltraCondensed);
+ }
+ if *self == Self::EXTRA_CONDENSED {
+ return Some(ExtraCondensed);
+ }
+ if *self == Self::CONDENSED {
+ return Some(Condensed);
+ }
+ if *self == Self::SEMI_CONDENSED {
+ return Some(SemiCondensed);
+ }
+ if *self == Self::NORMAL {
+ return Some(Normal);
+ }
+ if *self == Self::SEMI_EXPANDED {
+ return Some(SemiExpanded);
+ }
+ if *self == Self::EXPANDED {
+ return Some(Expanded);
+ }
+ if *self == Self::EXTRA_EXPANDED {
+ return Some(ExtraExpanded);
+ }
+ if *self == Self::ULTRA_EXPANDED {
+ return Some(UltraExpanded);
+ }
+ None
+ }
+}
+
+impl ToCss for FontStretch {
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: fmt::Write,
+ {
+ self.to_percentage().to_css(dest)
+ }
+}
+
+impl ToAnimatedValue for FontStretch {
+ type AnimatedValue = Percentage;
+
+ #[inline]
+ fn to_animated_value(self) -> Self::AnimatedValue {
+ self.to_percentage()
+ }
+
+ #[inline]
+ fn from_animated_value(animated: Self::AnimatedValue) -> Self {
+ Self::from_percentage(animated.0)
+ }
+}
+
+/// A computed value for the `line-height` property.
+pub type LineHeight = generics::GenericLineHeight<NonNegativeNumber, NonNegativeLength>;
+
+impl ToResolvedValue for LineHeight {
+ type ResolvedValue = Self;
+
+ fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {
+ // Resolve <number> to an absolute <length> based on font size.
+ if matches!(self, Self::Normal | Self::MozBlockHeight) {
+ return self;
+ }
+ let wm = context.style.writing_mode;
+ Self::Length(context.device.calc_line_height(
+ context.style.get_font(),
+ wm,
+ Some(context.element_info.element),
+ ))
+ }
+
+ #[inline]
+ fn from_resolved_value(value: Self::ResolvedValue) -> Self {
+ value
+ }
+}
diff --git a/servo/components/style/values/computed/image.rs b/servo/components/style/values/computed/image.rs
new file mode 100644
index 0000000000..8a91d95313
--- /dev/null
+++ b/servo/components/style/values/computed/image.rs
@@ -0,0 +1,205 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! CSS handling for the computed value of
+//! [`image`][image]s
+//!
+//! [image]: https://drafts.csswg.org/css-images/#image-values
+
+use crate::values::computed::percentage::Percentage;
+use crate::values::computed::position::Position;
+use crate::values::computed::url::ComputedImageUrl;
+use crate::values::computed::{Angle, Color, Context};
+use crate::values::computed::{
+ AngleOrPercentage, LengthPercentage, NonNegativeLength, NonNegativeLengthPercentage,
+ Resolution, ToComputedValue,
+};
+use crate::values::generics::image::{self as generic, GradientCompatMode};
+use crate::values::specified::image as specified;
+use crate::values::specified::position::{HorizontalPositionKeyword, VerticalPositionKeyword};
+use std::f32::consts::PI;
+use std::fmt::{self, Write};
+use style_traits::{CssWriter, ToCss};
+
+pub use specified::ImageRendering;
+
+/// Computed values for an image according to CSS-IMAGES.
+/// <https://drafts.csswg.org/css-images/#image-values>
+pub type Image = generic::GenericImage<Gradient, ComputedImageUrl, Color, Percentage, Resolution>;
+
+// Images should remain small, see https://github.com/servo/servo/pull/18430
+size_of_test!(Image, 16);
+
+/// Computed values for a CSS gradient.
+/// <https://drafts.csswg.org/css-images/#gradients>
+pub type Gradient = generic::GenericGradient<
+ LineDirection,
+ LengthPercentage,
+ NonNegativeLength,
+ NonNegativeLengthPercentage,
+ Position,
+ Angle,
+ AngleOrPercentage,
+ Color,
+>;
+
+/// Computed values for CSS cross-fade
+/// <https://drafts.csswg.org/css-images-4/#cross-fade-function>
+pub type CrossFade = generic::CrossFade<Image, Color, Percentage>;
+
+/// A computed radial gradient ending shape.
+pub type EndingShape = generic::GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage>;
+
+/// A computed gradient line direction.
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToResolvedValue)]
+#[repr(C, u8)]
+pub enum LineDirection {
+ /// An angle.
+ Angle(Angle),
+ /// A horizontal direction.
+ Horizontal(HorizontalPositionKeyword),
+ /// A vertical direction.
+ Vertical(VerticalPositionKeyword),
+ /// A corner.
+ Corner(HorizontalPositionKeyword, VerticalPositionKeyword),
+}
+
+/// The computed value for an `image-set()` image.
+pub type ImageSet = generic::GenericImageSet<Image, Resolution>;
+
+impl ToComputedValue for specified::ImageSet {
+ type ComputedValue = ImageSet;
+
+ fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+ let items = self.items.to_computed_value(context);
+ let dpr = context.device().device_pixel_ratio().get();
+
+ let mut supported_image = false;
+ let mut selected_index = std::usize::MAX;
+ let mut selected_resolution = 0.0;
+
+ for (i, item) in items.iter().enumerate() {
+ if item.has_mime_type && !context.device().is_supported_mime_type(&item.mime_type) {
+ // If the MIME type is not supported, we discard the ImageSetItem.
+ continue;
+ }
+
+ let candidate_resolution = item.resolution.dppx();
+ debug_assert!(
+ candidate_resolution >= 0.0,
+ "Resolutions should be non-negative"
+ );
+ if candidate_resolution == 0.0 {
+ // If the resolution is 0, we also treat it as an invalid image.
+ continue;
+ }
+
+ // https://drafts.csswg.org/css-images-4/#image-set-notation:
+ //
+ // Make a UA-specific choice of which to load, based on whatever criteria deemed
+ // relevant (such as the resolution of the display, connection speed, etc).
+ //
+ // For now, select the lowest resolution greater than display density, otherwise the
+ // greatest resolution available.
+ let better_candidate = || {
+ if selected_resolution < dpr && candidate_resolution > selected_resolution {
+ return true;
+ }
+ if candidate_resolution < selected_resolution && candidate_resolution >= dpr {
+ return true;
+ }
+ false
+ };
+
+ // The first item with a supported MIME type is obviously the current best candidate
+ if !supported_image || better_candidate() {
+ supported_image = true;
+ selected_index = i;
+ selected_resolution = candidate_resolution;
+ }
+ }
+
+ ImageSet {
+ selected_index,
+ items,
+ }
+ }
+
+ fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+ Self {
+ selected_index: std::usize::MAX,
+ items: ToComputedValue::from_computed_value(&computed.items),
+ }
+ }
+}
+
+impl generic::LineDirection for LineDirection {
+ fn points_downwards(&self, compat_mode: GradientCompatMode) -> bool {
+ match *self {
+ LineDirection::Angle(angle) => angle.radians() == PI,
+ LineDirection::Vertical(VerticalPositionKeyword::Bottom) => {
+ compat_mode == GradientCompatMode::Modern
+ },
+ LineDirection::Vertical(VerticalPositionKeyword::Top) => {
+ compat_mode != GradientCompatMode::Modern
+ },
+ _ => false,
+ }
+ }
+
+ fn to_css<W>(&self, dest: &mut CssWriter<W>, compat_mode: GradientCompatMode) -> fmt::Result
+ where
+ W: Write,
+ {
+ match *self {
+ LineDirection::Angle(ref angle) => angle.to_css(dest),
+ LineDirection::Horizontal(x) => {
+ if compat_mode == GradientCompatMode::Modern {
+ dest.write_str("to ")?;
+ }
+ x.to_css(dest)
+ },
+ LineDirection::Vertical(y) => {
+ if compat_mode == GradientCompatMode::Modern {
+ dest.write_str("to ")?;
+ }
+ y.to_css(dest)
+ },
+ LineDirection::Corner(x, y) => {
+ if compat_mode == GradientCompatMode::Modern {
+ dest.write_str("to ")?;
+ }
+ x.to_css(dest)?;
+ dest.write_char(' ')?;
+ y.to_css(dest)
+ },
+ }
+ }
+}
+
+impl ToComputedValue for specified::LineDirection {
+ type ComputedValue = LineDirection;
+
+ fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+ match *self {
+ specified::LineDirection::Angle(ref angle) => {
+ LineDirection::Angle(angle.to_computed_value(context))
+ },
+ specified::LineDirection::Horizontal(x) => LineDirection::Horizontal(x),
+ specified::LineDirection::Vertical(y) => LineDirection::Vertical(y),
+ specified::LineDirection::Corner(x, y) => LineDirection::Corner(x, y),
+ }
+ }
+
+ fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+ match *computed {
+ LineDirection::Angle(ref angle) => {
+ specified::LineDirection::Angle(ToComputedValue::from_computed_value(angle))
+ },
+ LineDirection::Horizontal(x) => specified::LineDirection::Horizontal(x),
+ LineDirection::Vertical(y) => specified::LineDirection::Vertical(y),
+ LineDirection::Corner(x, y) => specified::LineDirection::Corner(x, y),
+ }
+ }
+}
diff --git a/servo/components/style/values/computed/length.rs b/servo/components/style/values/computed/length.rs
new file mode 100644
index 0000000000..e75676a76d
--- /dev/null
+++ b/servo/components/style/values/computed/length.rs
@@ -0,0 +1,531 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! `<length>` computed values, and related ones.
+
+use super::{Context, Number, ToComputedValue};
+use crate::values::animated::ToAnimatedValue;
+use crate::values::computed::NonNegativeNumber;
+use crate::values::generics::length as generics;
+use crate::values::generics::length::{
+ GenericLengthOrNumber, GenericLengthPercentageOrNormal, GenericMaxSize, GenericSize,
+};
+use crate::values::generics::NonNegative;
+use crate::values::specified::length::{AbsoluteLength, FontBaseSize, LineHeightBase};
+use crate::values::{specified, CSSFloat};
+use crate::Zero;
+use app_units::Au;
+use std::fmt::{self, Write};
+use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub};
+use style_traits::{CSSPixel, CssWriter, ToCss};
+
+pub use super::image::Image;
+pub use super::length_percentage::{LengthPercentage, NonNegativeLengthPercentage};
+pub use crate::values::specified::url::UrlOrNone;
+pub use crate::values::specified::{Angle, BorderStyle, Time};
+
+impl ToComputedValue for specified::NoCalcLength {
+ type ComputedValue = Length;
+
+ #[inline]
+ fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+ self.to_computed_value_with_base_size(
+ context,
+ FontBaseSize::CurrentStyle,
+ LineHeightBase::CurrentStyle,
+ )
+ }
+
+ #[inline]
+ fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+ Self::Absolute(AbsoluteLength::Px(computed.px()))
+ }
+}
+
+impl specified::NoCalcLength {
+ /// Computes a length with a given font-relative base size.
+ pub fn to_computed_value_with_base_size(
+ &self,
+ context: &Context,
+ base_size: FontBaseSize,
+ line_height_base: LineHeightBase,
+ ) -> Length {
+ match *self {
+ Self::Absolute(length) => length.to_computed_value(context),
+ Self::FontRelative(length) => {
+ length.to_computed_value(context, base_size, line_height_base)
+ },
+ Self::ViewportPercentage(length) => length.to_computed_value(context),
+ Self::ContainerRelative(length) => length.to_computed_value(context),
+ Self::ServoCharacterWidth(length) => length
+ .to_computed_value(context.style().get_font().clone_font_size().computed_size()),
+ }
+ }
+}
+
+impl ToComputedValue for specified::Length {
+ type ComputedValue = Length;
+
+ #[inline]
+ fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+ match *self {
+ Self::NoCalc(l) => l.to_computed_value(context),
+ Self::Calc(ref calc) => {
+ let result = calc.to_computed_value(context);
+ debug_assert!(
+ result.to_length().is_some(),
+ "{:?} didn't resolve to a length: {:?}",
+ calc,
+ result,
+ );
+ result.to_length().unwrap_or_else(Length::zero)
+ },
+ }
+ }
+
+ #[inline]
+ fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+ Self::NoCalc(specified::NoCalcLength::from_computed_value(computed))
+ }
+}
+
+/// Some boilerplate to share between negative and non-negative
+/// length-percentage or auto.
+macro_rules! computed_length_percentage_or_auto {
+ ($inner:ty) => {
+ /// Returns the used value.
+ #[inline]
+ pub fn to_used_value(&self, percentage_basis: Au) -> Option<Au> {
+ match *self {
+ Self::Auto => None,
+ Self::LengthPercentage(ref lp) => Some(lp.to_used_value(percentage_basis)),
+ }
+ }
+
+ /// Returns true if the computed value is absolute 0 or 0%.
+ #[inline]
+ pub fn is_definitely_zero(&self) -> bool {
+ use crate::values::generics::length::LengthPercentageOrAuto::*;
+ match *self {
+ LengthPercentage(ref l) => l.is_definitely_zero(),
+ Auto => false,
+ }
+ }
+ };
+}
+
+/// A computed type for `<length-percentage> | auto`.
+pub type LengthPercentageOrAuto = generics::GenericLengthPercentageOrAuto<LengthPercentage>;
+
+impl LengthPercentageOrAuto {
+ /// Clamps the value to a non-negative value.
+ pub fn clamp_to_non_negative(self) -> Self {
+ use crate::values::generics::length::LengthPercentageOrAuto::*;
+ match self {
+ LengthPercentage(l) => LengthPercentage(l.clamp_to_non_negative()),
+ Auto => Auto,
+ }
+ }
+
+ /// Convert to have a borrow inside the enum
+ pub fn as_ref(&self) -> generics::GenericLengthPercentageOrAuto<&LengthPercentage> {
+ use crate::values::generics::length::LengthPercentageOrAuto::*;
+ match *self {
+ LengthPercentage(ref lp) => LengthPercentage(lp),
+ Auto => Auto,
+ }
+ }
+
+ computed_length_percentage_or_auto!(LengthPercentage);
+}
+
+impl generics::GenericLengthPercentageOrAuto<&LengthPercentage> {
+ /// Resolves the percentage.
+ #[inline]
+ pub fn percentage_relative_to(&self, basis: Length) -> LengthOrAuto {
+ use crate::values::generics::length::LengthPercentageOrAuto::*;
+ match self {
+ LengthPercentage(length_percentage) => {
+ LengthPercentage(length_percentage.percentage_relative_to(basis))
+ },
+ Auto => Auto,
+ }
+ }
+
+ /// Maybe resolves the percentage.
+ #[inline]
+ pub fn maybe_percentage_relative_to(&self, basis: Option<Length>) -> LengthOrAuto {
+ use crate::values::generics::length::LengthPercentageOrAuto::*;
+ match self {
+ LengthPercentage(length_percentage) => length_percentage
+ .maybe_percentage_relative_to(basis)
+ .map_or(Auto, LengthPercentage),
+ Auto => Auto,
+ }
+ }
+}
+
+/// A wrapper of LengthPercentageOrAuto, whose value must be >= 0.
+pub type NonNegativeLengthPercentageOrAuto =
+ generics::GenericLengthPercentageOrAuto<NonNegativeLengthPercentage>;
+
+impl NonNegativeLengthPercentageOrAuto {
+ computed_length_percentage_or_auto!(NonNegativeLengthPercentage);
+}
+
+#[cfg(feature = "servo")]
+impl MaxSize {
+ /// Convert the computed value into used value.
+ #[inline]
+ pub fn to_used_value(&self, percentage_basis: Au) -> Option<Au> {
+ match *self {
+ GenericMaxSize::None => None,
+ GenericMaxSize::LengthPercentage(ref lp) => Some(lp.to_used_value(percentage_basis)),
+ }
+ }
+}
+
+impl Size {
+ /// Convert the computed value into used value.
+ #[inline]
+ #[cfg(feature = "servo")]
+ pub fn to_used_value(&self, percentage_basis: Au) -> Option<Au> {
+ match *self {
+ GenericSize::Auto => None,
+ GenericSize::LengthPercentage(ref lp) => Some(lp.to_used_value(percentage_basis)),
+ }
+ }
+
+ /// Returns true if the computed value is absolute 0 or 0%.
+ #[inline]
+ pub fn is_definitely_zero(&self) -> bool {
+ match *self {
+ Self::Auto => false,
+ Self::LengthPercentage(ref lp) => lp.is_definitely_zero(),
+ #[cfg(feature = "gecko")]
+ Self::MinContent |
+ Self::MaxContent |
+ Self::FitContent |
+ Self::MozAvailable |
+ Self::FitContentFunction(_) => false,
+ }
+ }
+}
+
+/// The computed `<length>` value.
+#[derive(
+ Animate,
+ Clone,
+ ComputeSquaredDistance,
+ Copy,
+ Deserialize,
+ MallocSizeOf,
+ PartialEq,
+ PartialOrd,
+ Serialize,
+ ToAnimatedValue,
+ ToAnimatedZero,
+ ToComputedValue,
+ ToResolvedValue,
+ ToShmem,
+)]
+#[repr(C)]
+pub struct CSSPixelLength(CSSFloat);
+
+impl fmt::Debug for CSSPixelLength {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ self.0.fmt(f)?;
+ f.write_str(" px")
+ }
+}
+
+impl CSSPixelLength {
+ /// Return a new CSSPixelLength.
+ #[inline]
+ pub fn new(px: CSSFloat) -> Self {
+ CSSPixelLength(px)
+ }
+
+ /// Returns a normalized (NaN turned to zero) version of this length.
+ #[inline]
+ pub fn normalized(self) -> Self {
+ Self::new(crate::values::normalize(self.0))
+ }
+
+ /// Returns a finite (normalized and clamped to float min and max) version of this length.
+ #[inline]
+ pub fn finite(self) -> Self {
+ Self::new(crate::values::normalize(self.0).min(f32::MAX).max(f32::MIN))
+ }
+
+ /// Scale the length by a given amount.
+ #[inline]
+ pub fn scale_by(self, scale: CSSFloat) -> Self {
+ CSSPixelLength(self.0 * scale)
+ }
+
+ /// Return the containing pixel value.
+ #[inline]
+ pub fn px(self) -> CSSFloat {
+ self.0
+ }
+
+ /// Return the length with app_unit i32 type.
+ #[inline]
+ pub fn to_i32_au(self) -> i32 {
+ Au::from(self).0
+ }
+
+ /// Return the absolute value of this length.
+ #[inline]
+ pub fn abs(self) -> Self {
+ CSSPixelLength::new(self.0.abs())
+ }
+
+ /// Return the clamped value of this length.
+ #[inline]
+ pub fn clamp_to_non_negative(self) -> Self {
+ CSSPixelLength::new(self.0.max(0.))
+ }
+
+ /// Returns the minimum between `self` and `other`.
+ #[inline]
+ pub fn min(self, other: Self) -> Self {
+ CSSPixelLength::new(self.0.min(other.0))
+ }
+
+ /// Returns the maximum between `self` and `other`.
+ #[inline]
+ pub fn max(self, other: Self) -> Self {
+ CSSPixelLength::new(self.0.max(other.0))
+ }
+
+ /// Sets `self` to the maximum between `self` and `other`.
+ #[inline]
+ pub fn max_assign(&mut self, other: Self) {
+ *self = self.max(other);
+ }
+
+ /// Clamp the value to a lower bound and an optional upper bound.
+ ///
+ /// Can be used for example with `min-width` and `max-width`.
+ #[inline]
+ pub fn clamp_between_extremums(self, min_size: Self, max_size: Option<Self>) -> Self {
+ self.clamp_below_max(max_size).max(min_size)
+ }
+
+ /// Clamp the value to an optional upper bound.
+ ///
+ /// Can be used for example with `max-width`.
+ #[inline]
+ pub fn clamp_below_max(self, max_size: Option<Self>) -> Self {
+ match max_size {
+ None => self,
+ Some(max_size) => self.min(max_size),
+ }
+ }
+}
+
+impl num_traits::Zero for CSSPixelLength {
+ fn zero() -> Self {
+ CSSPixelLength::new(0.)
+ }
+
+ fn is_zero(&self) -> bool {
+ self.px() == 0.
+ }
+}
+
+impl ToCss for CSSPixelLength {
+ #[inline]
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: Write,
+ {
+ self.0.to_css(dest)?;
+ dest.write_str("px")
+ }
+}
+
+impl std::iter::Sum for CSSPixelLength {
+ fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
+ iter.fold(Length::zero(), Add::add)
+ }
+}
+
+impl Add for CSSPixelLength {
+ type Output = Self;
+
+ #[inline]
+ fn add(self, other: Self) -> Self {
+ Self::new(self.px() + other.px())
+ }
+}
+
+impl AddAssign for CSSPixelLength {
+ #[inline]
+ fn add_assign(&mut self, other: Self) {
+ self.0 += other.0;
+ }
+}
+
+impl Div for CSSPixelLength {
+ type Output = CSSFloat;
+
+ #[inline]
+ fn div(self, other: Self) -> CSSFloat {
+ self.px() / other.px()
+ }
+}
+
+impl Div<CSSFloat> for CSSPixelLength {
+ type Output = Self;
+
+ #[inline]
+ fn div(self, other: CSSFloat) -> Self {
+ Self::new(self.px() / other)
+ }
+}
+
+impl MulAssign<CSSFloat> for CSSPixelLength {
+ #[inline]
+ fn mul_assign(&mut self, other: CSSFloat) {
+ self.0 *= other;
+ }
+}
+
+impl Mul<CSSFloat> for CSSPixelLength {
+ type Output = Self;
+
+ #[inline]
+ fn mul(self, other: CSSFloat) -> Self {
+ Self::new(self.px() * other)
+ }
+}
+
+impl Neg for CSSPixelLength {
+ type Output = Self;
+
+ #[inline]
+ fn neg(self) -> Self {
+ CSSPixelLength::new(-self.0)
+ }
+}
+
+impl Sub for CSSPixelLength {
+ type Output = Self;
+
+ #[inline]
+ fn sub(self, other: Self) -> Self {
+ Self::new(self.px() - other.px())
+ }
+}
+
+impl From<CSSPixelLength> for Au {
+ #[inline]
+ fn from(len: CSSPixelLength) -> Self {
+ Au::from_f32_px(len.0)
+ }
+}
+
+impl From<Au> for CSSPixelLength {
+ #[inline]
+ fn from(len: Au) -> Self {
+ CSSPixelLength::new(len.to_f32_px())
+ }
+}
+
+impl From<CSSPixelLength> for euclid::Length<CSSFloat, CSSPixel> {
+ #[inline]
+ fn from(length: CSSPixelLength) -> Self {
+ Self::new(length.0)
+ }
+}
+
+/// An alias of computed `<length>` value.
+pub type Length = CSSPixelLength;
+
+/// Either a computed `<length>` or the `auto` keyword.
+pub type LengthOrAuto = generics::GenericLengthPercentageOrAuto<Length>;
+
+/// Either a non-negative `<length>` or the `auto` keyword.
+pub type NonNegativeLengthOrAuto = generics::GenericLengthPercentageOrAuto<NonNegativeLength>;
+
+/// Either a computed `<length>` or a `<number>` value.
+pub type LengthOrNumber = GenericLengthOrNumber<Length, Number>;
+
+/// A wrapper of Length, whose value must be >= 0.
+pub type NonNegativeLength = NonNegative<Length>;
+
+impl ToAnimatedValue for NonNegativeLength {
+ type AnimatedValue = Length;
+
+ #[inline]
+ fn to_animated_value(self) -> Self::AnimatedValue {
+ self.0
+ }
+
+ #[inline]
+ fn from_animated_value(animated: Self::AnimatedValue) -> Self {
+ NonNegativeLength::new(animated.px().max(0.))
+ }
+}
+
+impl NonNegativeLength {
+ /// Create a NonNegativeLength.
+ #[inline]
+ pub fn new(px: CSSFloat) -> Self {
+ NonNegative(Length::new(px.max(0.)))
+ }
+
+ /// Return the pixel value of |NonNegativeLength|.
+ #[inline]
+ pub fn px(&self) -> CSSFloat {
+ self.0.px()
+ }
+
+ #[inline]
+ /// Ensures it is non negative
+ pub fn clamp(self) -> Self {
+ if (self.0).0 < 0. {
+ Self::zero()
+ } else {
+ self
+ }
+ }
+}
+
+impl From<Length> for NonNegativeLength {
+ #[inline]
+ fn from(len: Length) -> Self {
+ NonNegative(len)
+ }
+}
+
+impl From<Au> for NonNegativeLength {
+ #[inline]
+ fn from(au: Au) -> Self {
+ NonNegative(au.into())
+ }
+}
+
+impl From<NonNegativeLength> for Au {
+ #[inline]
+ fn from(non_negative_len: NonNegativeLength) -> Self {
+ Au::from(non_negative_len.0)
+ }
+}
+
+/// Either a computed NonNegativeLengthPercentage or the `normal` keyword.
+pub type NonNegativeLengthPercentageOrNormal =
+ GenericLengthPercentageOrNormal<NonNegativeLengthPercentage>;
+
+/// Either a non-negative `<length>` or a `<number>`.
+pub type NonNegativeLengthOrNumber = GenericLengthOrNumber<NonNegativeLength, NonNegativeNumber>;
+
+/// A computed value for `min-width`, `min-height`, `width` or `height` property.
+pub type Size = GenericSize<NonNegativeLengthPercentage>;
+
+/// A computed value for `max-width` or `min-height` property.
+pub type MaxSize = GenericMaxSize<NonNegativeLengthPercentage>;
diff --git a/servo/components/style/values/computed/length_percentage.rs b/servo/components/style/values/computed/length_percentage.rs
new file mode 100644
index 0000000000..898281a7ef
--- /dev/null
+++ b/servo/components/style/values/computed/length_percentage.rs
@@ -0,0 +1,1055 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! `<length-percentage>` computed values, and related ones.
+//!
+//! The over-all design is a tagged pointer, with the lower bits of the pointer
+//! being non-zero if it is a non-calc value.
+//!
+//! It is expected to take 64 bits both in x86 and x86-64. This is implemented
+//! as a `union`, with 4 different variants:
+//!
+//! * The length and percentage variants have a { tag, f32 } (effectively)
+//! layout. The tag has to overlap with the lower 2 bits of the calc variant.
+//!
+//! * The `calc()` variant is a { tag, pointer } in x86 (so same as the
+//! others), or just a { pointer } in x86-64 (so that the two bits of the tag
+//! can be obtained from the lower bits of the pointer).
+//!
+//! * There's a `tag` variant just to make clear when only the tag is intended
+//! to be read. Note that the tag needs to be masked always by `TAG_MASK`, to
+//! deal with the pointer variant in x86-64.
+//!
+//! The assertions in the constructor methods ensure that the tag getter matches
+//! our expectations.
+
+use super::{Context, Length, Percentage, ToComputedValue};
+use crate::gecko_bindings::structs::GeckoFontMetrics;
+use crate::values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
+use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
+use crate::values::generics::calc::{CalcUnits, PositivePercentageBasis};
+use crate::values::generics::{calc, NonNegative};
+use crate::values::specified::length::{FontBaseSize, LineHeightBase};
+use crate::values::{specified, CSSFloat};
+use crate::{Zero, ZeroNoPercent};
+use app_units::Au;
+use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
+use serde::{Deserialize, Serialize};
+use std::borrow::Cow;
+use std::fmt::{self, Write};
+use style_traits::values::specified::AllowedNumericType;
+use style_traits::{CssWriter, ToCss};
+
+#[doc(hidden)]
+#[derive(Clone, Copy)]
+#[repr(C)]
+pub struct LengthVariant {
+ tag: u8,
+ length: Length,
+}
+
+#[doc(hidden)]
+#[derive(Clone, Copy)]
+#[repr(C)]
+pub struct PercentageVariant {
+ tag: u8,
+ percentage: Percentage,
+}
+
+// NOTE(emilio): cbindgen only understands the #[cfg] on the top level
+// definition.
+#[doc(hidden)]
+#[derive(Clone, Copy)]
+#[repr(C)]
+#[cfg(target_pointer_width = "32")]
+pub struct CalcVariant {
+ tag: u8,
+ ptr: *mut CalcLengthPercentage,
+}
+
+#[doc(hidden)]
+#[derive(Clone, Copy)]
+#[repr(C)]
+#[cfg(target_pointer_width = "64")]
+pub struct CalcVariant {
+ ptr: usize, // In little-endian byte order
+}
+
+// `CalcLengthPercentage` is `Send + Sync` as asserted below.
+unsafe impl Send for CalcVariant {}
+unsafe impl Sync for CalcVariant {}
+
+#[doc(hidden)]
+#[derive(Clone, Copy)]
+#[repr(C)]
+pub struct TagVariant {
+ tag: u8,
+}
+
+/// A `<length-percentage>` value. This can be either a `<length>`, a
+/// `<percentage>`, or a combination of both via `calc()`.
+///
+/// cbindgen:private-default-tagged-enum-constructor=false
+/// cbindgen:derive-mut-casts=true
+///
+/// https://drafts.csswg.org/css-values-4/#typedef-length-percentage
+///
+/// The tag is stored in the lower two bits.
+///
+/// We need to use a struct instead of the union directly because unions with
+/// Drop implementations are unstable, looks like.
+///
+/// Also we need the union and the variants to be `pub` (even though the member
+/// is private) so that cbindgen generates it. They're not part of the public
+/// API otherwise.
+#[repr(transparent)]
+pub struct LengthPercentage(LengthPercentageUnion);
+
+#[doc(hidden)]
+#[repr(C)]
+pub union LengthPercentageUnion {
+ length: LengthVariant,
+ percentage: PercentageVariant,
+ calc: CalcVariant,
+ tag: TagVariant,
+}
+
+impl LengthPercentageUnion {
+ #[doc(hidden)] // Need to be public so that cbindgen generates it.
+ pub const TAG_CALC: u8 = 0;
+ #[doc(hidden)]
+ pub const TAG_LENGTH: u8 = 1;
+ #[doc(hidden)]
+ pub const TAG_PERCENTAGE: u8 = 2;
+ #[doc(hidden)]
+ pub const TAG_MASK: u8 = 0b11;
+}
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+#[repr(u8)]
+enum Tag {
+ Calc = LengthPercentageUnion::TAG_CALC,
+ Length = LengthPercentageUnion::TAG_LENGTH,
+ Percentage = LengthPercentageUnion::TAG_PERCENTAGE,
+}
+
+// All the members should be 64 bits, even in 32-bit builds.
+#[allow(unused)]
+unsafe fn static_assert() {
+ fn assert_send_and_sync<T: Send + Sync>() {}
+ std::mem::transmute::<u64, LengthVariant>(0u64);
+ std::mem::transmute::<u64, PercentageVariant>(0u64);
+ std::mem::transmute::<u64, CalcVariant>(0u64);
+ std::mem::transmute::<u64, LengthPercentage>(0u64);
+ assert_send_and_sync::<LengthVariant>();
+ assert_send_and_sync::<PercentageVariant>();
+ assert_send_and_sync::<CalcLengthPercentage>();
+}
+
+impl Drop for LengthPercentage {
+ fn drop(&mut self) {
+ if self.tag() == Tag::Calc {
+ let _ = unsafe { Box::from_raw(self.calc_ptr()) };
+ }
+ }
+}
+
+impl MallocSizeOf for LengthPercentage {
+ fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
+ match self.unpack() {
+ Unpacked::Length(..) | Unpacked::Percentage(..) => 0,
+ Unpacked::Calc(c) => unsafe { ops.malloc_size_of(c) },
+ }
+ }
+}
+
+/// An unpacked `<length-percentage>` that borrows the `calc()` variant.
+#[derive(Clone, Debug, PartialEq, ToCss)]
+enum Unpacked<'a> {
+ Calc(&'a CalcLengthPercentage),
+ Length(Length),
+ Percentage(Percentage),
+}
+
+/// An unpacked `<length-percentage>` that mutably borrows the `calc()` variant.
+enum UnpackedMut<'a> {
+ Calc(&'a mut CalcLengthPercentage),
+ Length(Length),
+ Percentage(Percentage),
+}
+
+/// An unpacked `<length-percentage>` that owns the `calc()` variant, for
+/// serialization purposes.
+#[derive(Deserialize, PartialEq, Serialize)]
+enum Serializable {
+ Calc(CalcLengthPercentage),
+ Length(Length),
+ Percentage(Percentage),
+}
+
+impl LengthPercentage {
+ /// 1px length value for SVG defaults
+ #[inline]
+ pub fn one() -> Self {
+ Self::new_length(Length::new(1.))
+ }
+
+ /// 0%
+ #[inline]
+ pub fn zero_percent() -> Self {
+ Self::new_percent(Percentage::zero())
+ }
+
+ fn to_calc_node(&self) -> Cow<CalcNode> {
+ match self.unpack() {
+ Unpacked::Length(l) => Cow::Owned(CalcNode::Leaf(CalcLengthPercentageLeaf::Length(l))),
+ Unpacked::Percentage(p) => {
+ Cow::Owned(CalcNode::Leaf(CalcLengthPercentageLeaf::Percentage(p)))
+ },
+ Unpacked::Calc(p) => Cow::Borrowed(&p.node),
+ }
+ }
+
+ /// Constructs a length value.
+ #[inline]
+ pub fn new_length(length: Length) -> Self {
+ let length = Self(LengthPercentageUnion {
+ length: LengthVariant {
+ tag: LengthPercentageUnion::TAG_LENGTH,
+ length,
+ },
+ });
+ debug_assert_eq!(length.tag(), Tag::Length);
+ length
+ }
+
+ /// Constructs a percentage value.
+ #[inline]
+ pub fn new_percent(percentage: Percentage) -> Self {
+ let percent = Self(LengthPercentageUnion {
+ percentage: PercentageVariant {
+ tag: LengthPercentageUnion::TAG_PERCENTAGE,
+ percentage,
+ },
+ });
+ debug_assert_eq!(percent.tag(), Tag::Percentage);
+ percent
+ }
+
+ /// Given a `LengthPercentage` value `v`, construct the value representing
+ /// `calc(100% - v)`.
+ pub fn hundred_percent_minus(v: Self, clamping_mode: AllowedNumericType) -> Self {
+ // TODO: This could in theory take ownership of the calc node in `v` if
+ // possible instead of cloning.
+ let mut node = v.to_calc_node().into_owned();
+ node.negate();
+
+ let new_node = CalcNode::Sum(
+ vec![
+ CalcNode::Leaf(CalcLengthPercentageLeaf::Percentage(Percentage::hundred())),
+ node,
+ ]
+ .into(),
+ );
+
+ Self::new_calc(new_node, clamping_mode)
+ }
+
+ /// Given a list of `LengthPercentage` values, construct the value representing
+ /// `calc(100% - the sum of the list)`.
+ pub fn hundred_percent_minus_list(list: &[&Self], clamping_mode: AllowedNumericType) -> Self {
+ let mut new_list = vec![CalcNode::Leaf(CalcLengthPercentageLeaf::Percentage(
+ Percentage::hundred(),
+ ))];
+
+ for lp in list.iter() {
+ let mut node = lp.to_calc_node().into_owned();
+ node.negate();
+ new_list.push(node)
+ }
+
+ Self::new_calc(CalcNode::Sum(new_list.into()), clamping_mode)
+ }
+
+ /// Constructs a `calc()` value.
+ #[inline]
+ pub fn new_calc(mut node: CalcNode, clamping_mode: AllowedNumericType) -> Self {
+ node.simplify_and_sort();
+
+ match node {
+ CalcNode::Leaf(l) => {
+ return match l {
+ CalcLengthPercentageLeaf::Length(l) => {
+ Self::new_length(Length::new(clamping_mode.clamp(l.px())).normalized())
+ },
+ CalcLengthPercentageLeaf::Percentage(p) => Self::new_percent(Percentage(
+ clamping_mode.clamp(crate::values::normalize(p.0)),
+ )),
+ CalcLengthPercentageLeaf::Number(number) => {
+ debug_assert!(
+ false,
+ "The final result of a <length-percentage> should never be a number"
+ );
+ Self::new_length(Length::new(number))
+ },
+ };
+ },
+ _ => Self::new_calc_unchecked(Box::new(CalcLengthPercentage {
+ clamping_mode,
+ node,
+ })),
+ }
+ }
+
+ /// Private version of new_calc() that constructs a calc() variant without
+ /// checking.
+ fn new_calc_unchecked(calc: Box<CalcLengthPercentage>) -> Self {
+ let ptr = Box::into_raw(calc);
+
+ #[cfg(target_pointer_width = "32")]
+ let calc = CalcVariant {
+ tag: LengthPercentageUnion::TAG_CALC,
+ ptr,
+ };
+
+ #[cfg(target_pointer_width = "64")]
+ let calc = CalcVariant {
+ #[cfg(target_endian = "little")]
+ ptr: ptr as usize,
+ #[cfg(target_endian = "big")]
+ ptr: (ptr as usize).swap_bytes(),
+ };
+
+ let calc = Self(LengthPercentageUnion { calc });
+ debug_assert_eq!(calc.tag(), Tag::Calc);
+ calc
+ }
+
+ #[inline]
+ fn tag(&self) -> Tag {
+ match unsafe { self.0.tag.tag & LengthPercentageUnion::TAG_MASK } {
+ LengthPercentageUnion::TAG_CALC => Tag::Calc,
+ LengthPercentageUnion::TAG_LENGTH => Tag::Length,
+ LengthPercentageUnion::TAG_PERCENTAGE => Tag::Percentage,
+ _ => unsafe { debug_unreachable!("Bogus tag?") },
+ }
+ }
+
+ #[inline]
+ fn unpack_mut<'a>(&'a mut self) -> UnpackedMut<'a> {
+ unsafe {
+ match self.tag() {
+ Tag::Calc => UnpackedMut::Calc(&mut *self.calc_ptr()),
+ Tag::Length => UnpackedMut::Length(self.0.length.length),
+ Tag::Percentage => UnpackedMut::Percentage(self.0.percentage.percentage),
+ }
+ }
+ }
+
+ #[inline]
+ fn unpack<'a>(&'a self) -> Unpacked<'a> {
+ unsafe {
+ match self.tag() {
+ Tag::Calc => Unpacked::Calc(&*self.calc_ptr()),
+ Tag::Length => Unpacked::Length(self.0.length.length),
+ Tag::Percentage => Unpacked::Percentage(self.0.percentage.percentage),
+ }
+ }
+ }
+
+ #[inline]
+ unsafe fn calc_ptr(&self) -> *mut CalcLengthPercentage {
+ #[cfg(not(all(target_endian = "big", target_pointer_width = "64")))]
+ {
+ self.0.calc.ptr as *mut _
+ }
+ #[cfg(all(target_endian = "big", target_pointer_width = "64"))]
+ {
+ self.0.calc.ptr.swap_bytes() as *mut _
+ }
+ }
+
+ #[inline]
+ fn to_serializable(&self) -> Serializable {
+ match self.unpack() {
+ Unpacked::Calc(c) => Serializable::Calc(c.clone()),
+ Unpacked::Length(l) => Serializable::Length(l),
+ Unpacked::Percentage(p) => Serializable::Percentage(p),
+ }
+ }
+
+ #[inline]
+ fn from_serializable(s: Serializable) -> Self {
+ match s {
+ Serializable::Calc(c) => Self::new_calc_unchecked(Box::new(c)),
+ Serializable::Length(l) => Self::new_length(l),
+ Serializable::Percentage(p) => Self::new_percent(p),
+ }
+ }
+
+ /// Returns true if the computed value is absolute 0 or 0%.
+ #[inline]
+ pub fn is_definitely_zero(&self) -> bool {
+ match self.unpack() {
+ Unpacked::Length(l) => l.px() == 0.0,
+ Unpacked::Percentage(p) => p.0 == 0.0,
+ Unpacked::Calc(..) => false,
+ }
+ }
+
+ /// Resolves the percentage.
+ #[inline]
+ pub fn resolve(&self, basis: Length) -> Length {
+ match self.unpack() {
+ Unpacked::Length(l) => l,
+ Unpacked::Percentage(p) => (basis * p.0).normalized(),
+ Unpacked::Calc(ref c) => c.resolve(basis),
+ }
+ }
+
+ /// Resolves the percentage. Just an alias of resolve().
+ #[inline]
+ pub fn percentage_relative_to(&self, basis: Length) -> Length {
+ self.resolve(basis)
+ }
+
+ /// Return whether there's any percentage in this value.
+ #[inline]
+ pub fn has_percentage(&self) -> bool {
+ match self.unpack() {
+ Unpacked::Length(..) => false,
+ Unpacked::Percentage(..) | Unpacked::Calc(..) => true,
+ }
+ }
+
+ /// Converts to a `<length>` if possible.
+ pub fn to_length(&self) -> Option<Length> {
+ match self.unpack() {
+ Unpacked::Length(l) => Some(l),
+ Unpacked::Percentage(..) | Unpacked::Calc(..) => {
+ debug_assert!(self.has_percentage());
+ return None;
+ },
+ }
+ }
+
+ /// Converts to a `<percentage>` if possible.
+ #[inline]
+ pub fn to_percentage(&self) -> Option<Percentage> {
+ match self.unpack() {
+ Unpacked::Percentage(p) => Some(p),
+ Unpacked::Length(..) | Unpacked::Calc(..) => None,
+ }
+ }
+
+ /// Returns the used value.
+ #[inline]
+ pub fn to_used_value(&self, containing_length: Au) -> Au {
+ Au::from(self.to_pixel_length(containing_length))
+ }
+
+ /// Returns the used value as CSSPixelLength.
+ #[inline]
+ pub fn to_pixel_length(&self, containing_length: Au) -> Length {
+ self.resolve(containing_length.into())
+ }
+
+ /// Convert the computed value into used value.
+ #[inline]
+ pub fn maybe_to_used_value(&self, container_len: Option<Length>) -> Option<Au> {
+ self.maybe_percentage_relative_to(container_len)
+ .map(Au::from)
+ }
+
+ /// If there are special rules for computing percentages in a value (e.g.
+ /// the height property), they apply whenever a calc() expression contains
+ /// percentages.
+ pub fn maybe_percentage_relative_to(&self, container_len: Option<Length>) -> Option<Length> {
+ if let Unpacked::Length(l) = self.unpack() {
+ return Some(l);
+ }
+ Some(self.resolve(container_len?))
+ }
+
+ /// Returns the clamped non-negative values.
+ #[inline]
+ pub fn clamp_to_non_negative(mut self) -> Self {
+ match self.unpack_mut() {
+ UnpackedMut::Length(l) => Self::new_length(l.clamp_to_non_negative()),
+ UnpackedMut::Percentage(p) => Self::new_percent(p.clamp_to_non_negative()),
+ UnpackedMut::Calc(ref mut c) => {
+ c.clamping_mode = AllowedNumericType::NonNegative;
+ self
+ },
+ }
+ }
+}
+
+impl PartialEq for LengthPercentage {
+ fn eq(&self, other: &Self) -> bool {
+ self.unpack() == other.unpack()
+ }
+}
+
+impl fmt::Debug for LengthPercentage {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ self.unpack().fmt(formatter)
+ }
+}
+
+impl ToAnimatedZero for LengthPercentage {
+ fn to_animated_zero(&self) -> Result<Self, ()> {
+ Ok(match self.unpack() {
+ Unpacked::Length(l) => Self::new_length(l.to_animated_zero()?),
+ Unpacked::Percentage(p) => Self::new_percent(p.to_animated_zero()?),
+ Unpacked::Calc(c) => Self::new_calc_unchecked(Box::new(c.to_animated_zero()?)),
+ })
+ }
+}
+
+impl Clone for LengthPercentage {
+ fn clone(&self) -> Self {
+ match self.unpack() {
+ Unpacked::Length(l) => Self::new_length(l),
+ Unpacked::Percentage(p) => Self::new_percent(p),
+ Unpacked::Calc(c) => Self::new_calc_unchecked(Box::new(c.clone())),
+ }
+ }
+}
+
+impl ToComputedValue for specified::LengthPercentage {
+ type ComputedValue = LengthPercentage;
+
+ fn to_computed_value(&self, context: &Context) -> LengthPercentage {
+ match *self {
+ specified::LengthPercentage::Length(ref value) => {
+ LengthPercentage::new_length(value.to_computed_value(context))
+ },
+ specified::LengthPercentage::Percentage(value) => LengthPercentage::new_percent(value),
+ specified::LengthPercentage::Calc(ref calc) => (**calc).to_computed_value(context),
+ }
+ }
+
+ fn from_computed_value(computed: &LengthPercentage) -> Self {
+ match computed.unpack() {
+ Unpacked::Length(ref l) => {
+ specified::LengthPercentage::Length(ToComputedValue::from_computed_value(l))
+ },
+ Unpacked::Percentage(p) => specified::LengthPercentage::Percentage(p),
+ Unpacked::Calc(c) => {
+ // We simplify before constructing the LengthPercentage if
+ // needed, so this is always fine.
+ specified::LengthPercentage::Calc(Box::new(
+ specified::CalcLengthPercentage::from_computed_value(c),
+ ))
+ },
+ }
+ }
+}
+
+impl ComputeSquaredDistance for LengthPercentage {
+ #[inline]
+ fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
+ // A somewhat arbitrary base, it doesn't really make sense to mix
+ // lengths with percentages, but we can't do much better here, and this
+ // ensures that the distance between length-only and percentage-only
+ // lengths makes sense.
+ let basis = Length::new(100.);
+ self.resolve(basis)
+ .compute_squared_distance(&other.resolve(basis))
+ }
+}
+
+impl ToCss for LengthPercentage {
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: Write,
+ {
+ self.unpack().to_css(dest)
+ }
+}
+
+impl Zero for LengthPercentage {
+ fn zero() -> Self {
+ LengthPercentage::new_length(Length::zero())
+ }
+
+ #[inline]
+ fn is_zero(&self) -> bool {
+ self.is_definitely_zero()
+ }
+}
+
+impl ZeroNoPercent for LengthPercentage {
+ #[inline]
+ fn is_zero_no_percent(&self) -> bool {
+ self.is_definitely_zero() && !self.has_percentage()
+ }
+}
+
+impl Serialize for LengthPercentage {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: serde::Serializer,
+ {
+ self.to_serializable().serialize(serializer)
+ }
+}
+
+impl<'de> Deserialize<'de> for LengthPercentage {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: serde::Deserializer<'de>,
+ {
+ Ok(Self::from_serializable(Serializable::deserialize(
+ deserializer,
+ )?))
+ }
+}
+
+/// The leaves of a `<length-percentage>` calc expression.
+#[derive(
+ Clone,
+ Debug,
+ Deserialize,
+ MallocSizeOf,
+ PartialEq,
+ Serialize,
+ ToAnimatedZero,
+ ToCss,
+ ToResolvedValue,
+)]
+#[allow(missing_docs)]
+#[repr(u8)]
+pub enum CalcLengthPercentageLeaf {
+ Length(Length),
+ Percentage(Percentage),
+ Number(f32),
+}
+
+impl CalcLengthPercentageLeaf {
+ fn is_zero_length(&self) -> bool {
+ match *self {
+ Self::Length(ref l) => l.is_zero(),
+ Self::Percentage(..) => false,
+ Self::Number(..) => false,
+ }
+ }
+}
+
+impl calc::CalcNodeLeaf for CalcLengthPercentageLeaf {
+ fn unit(&self) -> CalcUnits {
+ match self {
+ Self::Length(_) => CalcUnits::LENGTH,
+ Self::Percentage(_) => CalcUnits::PERCENTAGE,
+ Self::Number(_) => CalcUnits::empty(),
+ }
+ }
+
+ fn unitless_value(&self) -> f32 {
+ match *self {
+ Self::Length(ref l) => l.px(),
+ Self::Percentage(ref p) => p.0,
+ Self::Number(n) => n,
+ }
+ }
+
+ fn new_number(value: f32) -> Self {
+ Self::Number(value)
+ }
+
+ fn as_number(&self) -> Option<f32> {
+ match *self {
+ Self::Length(_) | Self::Percentage(_) => None,
+ Self::Number(value) => Some(value),
+ }
+ }
+
+ fn compare(&self, other: &Self, basis: PositivePercentageBasis) -> Option<std::cmp::Ordering> {
+ use self::CalcLengthPercentageLeaf::*;
+ if std::mem::discriminant(self) != std::mem::discriminant(other) {
+ return None;
+ }
+
+ if matches!(self, Percentage(..)) && matches!(basis, PositivePercentageBasis::Unknown) {
+ return None;
+ }
+
+ let self_negative = self.is_negative();
+ if self_negative != other.is_negative() {
+ return Some(if self_negative { std::cmp::Ordering::Less } else { std::cmp::Ordering::Greater });
+ }
+
+ match (self, other) {
+ (&Length(ref one), &Length(ref other)) => one.partial_cmp(other),
+ (&Percentage(ref one), &Percentage(ref other)) => one.partial_cmp(other),
+ (&Number(ref one), &Number(ref other)) => one.partial_cmp(other),
+ _ => unsafe {
+ match *self {
+ Length(..) | Percentage(..) | Number(..) => {},
+ }
+ debug_unreachable!("Forgot to handle unit in compare()")
+ },
+ }
+ }
+
+ fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()> {
+ use self::CalcLengthPercentageLeaf::*;
+
+ // 0px plus anything else is equal to the right hand side.
+ if self.is_zero_length() {
+ *self = other.clone();
+ return Ok(());
+ }
+
+ if other.is_zero_length() {
+ return Ok(());
+ }
+
+ if std::mem::discriminant(self) != std::mem::discriminant(other) {
+ return Err(());
+ }
+
+ match (self, other) {
+ (&mut Length(ref mut one), &Length(ref other)) => {
+ *one += *other;
+ },
+ (&mut Percentage(ref mut one), &Percentage(ref other)) => {
+ one.0 += other.0;
+ },
+ (&mut Number(ref mut one), &Number(ref other)) => {
+ *one += *other;
+ },
+ _ => unsafe {
+ match *other {
+ Length(..) | Percentage(..) | Number(..) => {},
+ }
+ debug_unreachable!("Forgot to handle unit in try_sum_in_place()")
+ },
+ }
+
+ Ok(())
+ }
+
+ fn try_product_in_place(&mut self, other: &mut Self) -> bool {
+ if let Self::Number(ref mut left) = *self {
+ if let Self::Number(ref right) = *other {
+ // Both sides are numbers, so we can just modify the left side.
+ *left *= *right;
+ true
+ } else {
+ // The right side is not a number, so the result should be in the units of the right
+ // side.
+ other.map(|v| v * *left);
+ std::mem::swap(self, other);
+ true
+ }
+ } else if let Self::Number(ref right) = *other {
+ // The left side is not a number, but the right side is, so the result is the left
+ // side unit.
+ self.map(|v| v * *right);
+ true
+ } else {
+ // Neither side is a number, so a product is not possible.
+ false
+ }
+ }
+
+ fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
+ where
+ O: Fn(f32, f32) -> f32,
+ {
+ use self::CalcLengthPercentageLeaf::*;
+ if std::mem::discriminant(self) != std::mem::discriminant(other) {
+ return Err(());
+ }
+ Ok(match (self, other) {
+ (&Length(ref one), &Length(ref other)) => {
+ Length(super::Length::new(op(one.px(), other.px())))
+ },
+ (&Percentage(one), &Percentage(other)) => {
+ Self::Percentage(super::Percentage(op(one.0, other.0)))
+ },
+ (&Number(one), &Number(other)) => Self::Number(op(one, other)),
+ _ => unsafe {
+ match *self {
+ Length(..) | Percentage(..) | Number(..) => {},
+ }
+ debug_unreachable!("Forgot to handle unit in try_op()")
+ },
+ })
+ }
+
+ fn map(&mut self, mut op: impl FnMut(f32) -> f32) {
+ match self {
+ Self::Length(value) => {
+ *value = Length::new(op(value.px()));
+ },
+ Self::Percentage(value) => {
+ *value = Percentage(op(value.0));
+ },
+ Self::Number(value) => {
+ *value = op(*value);
+ },
+ }
+ }
+
+ fn simplify(&mut self) {}
+
+ fn sort_key(&self) -> calc::SortKey {
+ match *self {
+ Self::Length(..) => calc::SortKey::Px,
+ Self::Percentage(..) => calc::SortKey::Percentage,
+ Self::Number(..) => calc::SortKey::Number,
+ }
+ }
+}
+
+/// The computed version of a calc() node for `<length-percentage>` values.
+pub type CalcNode = calc::GenericCalcNode<CalcLengthPercentageLeaf>;
+
+/// The representation of a calc() function with mixed lengths and percentages.
+#[derive(
+ Clone, Debug, Deserialize, MallocSizeOf, Serialize, ToAnimatedZero, ToResolvedValue, ToCss,
+)]
+#[repr(C)]
+pub struct CalcLengthPercentage {
+ #[animation(constant)]
+ #[css(skip)]
+ clamping_mode: AllowedNumericType,
+ node: CalcNode,
+}
+
+impl CalcLengthPercentage {
+ /// Resolves the percentage.
+ #[inline]
+ pub fn resolve(&self, basis: Length) -> Length {
+ // unwrap() is fine because the conversion below is infallible.
+ if let CalcLengthPercentageLeaf::Length(px) = self
+ .node
+ .resolve_map(|leaf| {
+ Ok(if let CalcLengthPercentageLeaf::Percentage(p) = leaf {
+ CalcLengthPercentageLeaf::Length(Length::new(basis.px() * p.0))
+ } else {
+ leaf.clone()
+ })
+ })
+ .unwrap()
+ {
+ Length::new(self.clamping_mode.clamp(px.px())).normalized()
+ } else {
+ unreachable!("resolve_map should turn percentages to lengths, and parsing should ensure that we don't end up with a number");
+ }
+ }
+}
+
+// NOTE(emilio): We don't compare `clamping_mode` since we want to preserve the
+// invariant that `from_computed_value(length).to_computed_value(..) == length`.
+//
+// Right now for e.g. a non-negative length, we set clamping_mode to `All`
+// unconditionally for non-calc values, and to `NonNegative` for calc.
+//
+// If we determine that it's sound, from_computed_value() can generate an
+// absolute length, which then would get `All` as the clamping mode.
+//
+// We may want to just eagerly-detect whether we can clamp in
+// `LengthPercentage::new` and switch to `AllowedNumericType::NonNegative` then,
+// maybe.
+impl PartialEq for CalcLengthPercentage {
+ fn eq(&self, other: &Self) -> bool {
+ self.node == other.node
+ }
+}
+
+impl specified::CalcLengthPercentage {
+ /// Compute the value, zooming any absolute units by the zoom function.
+ fn to_computed_value_with_zoom<F>(
+ &self,
+ context: &Context,
+ zoom_fn: F,
+ base_size: FontBaseSize,
+ line_height_base: LineHeightBase,
+ ) -> LengthPercentage
+ where
+ F: Fn(Length) -> Length,
+ {
+ use crate::values::specified::calc::Leaf;
+
+ let node = self.node.map_leaves(|leaf| match *leaf {
+ Leaf::Percentage(p) => CalcLengthPercentageLeaf::Percentage(Percentage(p)),
+ Leaf::Length(l) => CalcLengthPercentageLeaf::Length({
+ let result =
+ l.to_computed_value_with_base_size(context, base_size, line_height_base);
+ if l.should_zoom_text() {
+ zoom_fn(result)
+ } else {
+ result
+ }
+ }),
+ Leaf::Number(n) => CalcLengthPercentageLeaf::Number(n),
+ Leaf::Angle(..) | Leaf::Time(..) | Leaf::Resolution(..) => {
+ unreachable!("Shouldn't have parsed")
+ },
+ });
+
+ LengthPercentage::new_calc(node, self.clamping_mode)
+ }
+
+ /// Compute font-size or line-height taking into account text-zoom if necessary.
+ pub fn to_computed_value_zoomed(
+ &self,
+ context: &Context,
+ base_size: FontBaseSize,
+ line_height_base: LineHeightBase,
+ ) -> LengthPercentage {
+ self.to_computed_value_with_zoom(
+ context,
+ |abs| context.maybe_zoom_text(abs),
+ base_size,
+ line_height_base,
+ )
+ }
+
+ /// Compute the value into pixel length as CSSFloat without context,
+ /// so it returns Err(()) if there is any non-absolute unit.
+ pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
+ use crate::values::specified::calc::Leaf;
+ use crate::values::specified::length::NoCalcLength;
+
+ // Simplification should've turned this into an absolute length,
+ // otherwise it wouldn't have been able to.
+ match self.node {
+ calc::CalcNode::Leaf(Leaf::Length(NoCalcLength::Absolute(ref l))) => Ok(l.to_px()),
+ _ => Err(()),
+ }
+ }
+
+ /// Compute the value into pixel length as CSSFloat, using the get_font_metrics function
+ /// if provided to resolve font-relative dimensions.
+ pub fn to_computed_pixel_length_with_font_metrics(
+ &self,
+ get_font_metrics: Option<impl Fn() -> GeckoFontMetrics>,
+ ) -> Result<CSSFloat, ()> {
+ use crate::values::specified::calc::Leaf;
+ use crate::values::specified::length::NoCalcLength;
+
+ match self.node {
+ calc::CalcNode::Leaf(Leaf::Length(NoCalcLength::Absolute(ref l))) => Ok(l.to_px()),
+ calc::CalcNode::Leaf(Leaf::Length(NoCalcLength::FontRelative(ref l))) => {
+ if let Some(getter) = get_font_metrics {
+ l.to_computed_pixel_length_with_font_metrics(getter)
+ } else {
+ Err(())
+ }
+ },
+ _ => Err(()),
+ }
+ }
+
+ /// Compute the calc using the current font-size and line-height. (and without text-zoom).
+ pub fn to_computed_value(&self, context: &Context) -> LengthPercentage {
+ self.to_computed_value_with_zoom(
+ context,
+ |abs| abs,
+ FontBaseSize::CurrentStyle,
+ LineHeightBase::CurrentStyle,
+ )
+ }
+
+ #[inline]
+ fn from_computed_value(computed: &CalcLengthPercentage) -> Self {
+ use crate::values::specified::calc::Leaf;
+ use crate::values::specified::length::NoCalcLength;
+
+ specified::CalcLengthPercentage {
+ clamping_mode: computed.clamping_mode,
+ node: computed.node.map_leaves(|l| match l {
+ CalcLengthPercentageLeaf::Length(ref l) => {
+ Leaf::Length(NoCalcLength::from_px(l.px()))
+ },
+ CalcLengthPercentageLeaf::Percentage(ref p) => Leaf::Percentage(p.0),
+ CalcLengthPercentageLeaf::Number(n) => Leaf::Number(*n),
+ }),
+ }
+ }
+}
+
+/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
+/// https://drafts.csswg.org/css-values-4/#combine-math
+/// https://drafts.csswg.org/css-values-4/#combine-mixed
+impl Animate for LengthPercentage {
+ #[inline]
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+ Ok(match (self.unpack(), other.unpack()) {
+ (Unpacked::Length(one), Unpacked::Length(other)) => {
+ Self::new_length(one.animate(&other, procedure)?)
+ },
+ (Unpacked::Percentage(one), Unpacked::Percentage(other)) => {
+ Self::new_percent(one.animate(&other, procedure)?)
+ },
+ _ => {
+ use calc::CalcNodeLeaf;
+
+ fn product_with(mut node: CalcNode, product: f32) -> CalcNode {
+ let mut number = CalcNode::Leaf(CalcLengthPercentageLeaf::new_number(product));
+ if !node.try_product_in_place(&mut number) {
+ CalcNode::Product(vec![node, number].into())
+ } else {
+ node
+ }
+ }
+
+ let (l, r) = procedure.weights();
+ let one = product_with(self.to_calc_node().into_owned(), l as f32);
+ let other = product_with(other.to_calc_node().into_owned(), r as f32);
+
+ Self::new_calc(
+ CalcNode::Sum(vec![one, other].into()),
+ AllowedNumericType::All,
+ )
+ },
+ })
+ }
+}
+
+/// A wrapper of LengthPercentage, whose value must be >= 0.
+pub type NonNegativeLengthPercentage = NonNegative<LengthPercentage>;
+
+impl ToAnimatedValue for NonNegativeLengthPercentage {
+ type AnimatedValue = LengthPercentage;
+
+ #[inline]
+ fn to_animated_value(self) -> Self::AnimatedValue {
+ self.0
+ }
+
+ #[inline]
+ fn from_animated_value(animated: Self::AnimatedValue) -> Self {
+ NonNegative(animated.clamp_to_non_negative())
+ }
+}
+
+impl NonNegativeLengthPercentage {
+ /// Returns true if the computed value is absolute 0 or 0%.
+ #[inline]
+ pub fn is_definitely_zero(&self) -> bool {
+ self.0.is_definitely_zero()
+ }
+
+ /// Returns the used value.
+ #[inline]
+ pub fn to_used_value(&self, containing_length: Au) -> Au {
+ let resolved = self.0.to_used_value(containing_length);
+ std::cmp::max(resolved, Au(0))
+ }
+
+ /// Convert the computed value into used value.
+ #[inline]
+ pub fn maybe_to_used_value(&self, containing_length: Option<Au>) -> Option<Au> {
+ let resolved = self
+ .0
+ .maybe_to_used_value(containing_length.map(|v| v.into()))?;
+ Some(std::cmp::max(resolved, Au(0)))
+ }
+}
diff --git a/servo/components/style/values/computed/list.rs b/servo/components/style/values/computed/list.rs
new file mode 100644
index 0000000000..3e5d1eb220
--- /dev/null
+++ b/servo/components/style/values/computed/list.rs
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! `list` computed values.
+
+#[cfg(feature = "gecko")]
+pub use crate::values::specified::list::ListStyleType;
+pub use crate::values::specified::list::Quotes;
+
+impl Quotes {
+ /// Initial value for `quotes`.
+ #[inline]
+ pub fn get_initial_value() -> Quotes {
+ Quotes::Auto
+ }
+}
diff --git a/servo/components/style/values/computed/mod.rs b/servo/components/style/values/computed/mod.rs
new file mode 100644
index 0000000000..de5db2cdab
--- /dev/null
+++ b/servo/components/style/values/computed/mod.rs
@@ -0,0 +1,1035 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Computed values.
+
+use self::transform::DirectionVector;
+use super::animated::ToAnimatedValue;
+use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
+use super::generics::grid::ImplicitGridTracks as GenericImplicitGridTracks;
+use super::generics::grid::{GenericGridLine, GenericTrackBreadth};
+use super::generics::grid::{GenericTrackSize, TrackList as GenericTrackList};
+use super::generics::transform::IsParallelTo;
+use super::generics::{self, GreaterThanOrEqualToOne, NonNegative, ZeroToOne};
+use super::specified;
+use super::{CSSFloat, CSSInteger};
+use crate::computed_value_flags::ComputedValueFlags;
+use crate::context::QuirksMode;
+use crate::custom_properties::ComputedCustomProperties;
+use crate::font_metrics::{FontMetrics, FontMetricsOrientation};
+use crate::media_queries::Device;
+#[cfg(feature = "gecko")]
+use crate::properties;
+use crate::properties::{ComputedValues, StyleBuilder};
+use crate::rule_cache::RuleCacheConditions;
+use crate::stylesheets::container_rule::{
+ ContainerInfo, ContainerSizeQuery, ContainerSizeQueryResult,
+};
+use crate::stylist::Stylist;
+use crate::values::specified::length::FontBaseSize;
+use crate::{ArcSlice, Atom, One};
+use euclid::{default, Point2D, Rect, Size2D};
+use servo_arc::Arc;
+use std::cell::RefCell;
+use std::cmp;
+use std::f32;
+use std::ops::{Add, Sub};
+
+#[cfg(feature = "gecko")]
+pub use self::align::{
+ AlignContent, AlignItems, AlignTracks, JustifyContent, JustifyItems, JustifyTracks,
+ SelfAlignment,
+};
+#[cfg(feature = "gecko")]
+pub use self::align::{AlignSelf, JustifySelf};
+pub use self::angle::Angle;
+pub use self::animation::{
+ AnimationIterationCount, AnimationName, AnimationTimeline, AnimationPlayState,
+ AnimationFillMode, AnimationComposition, AnimationDirection, ScrollAxis,
+ ScrollTimelineName, TransitionProperty, ViewTimelineInset
+};
+pub use self::background::{BackgroundRepeat, BackgroundSize};
+pub use self::basic_shape::FillRule;
+pub use self::border::{
+ BorderCornerRadius, BorderImageRepeat, BorderImageSideWidth, BorderImageSlice,
+ BorderImageWidth, BorderRadius, BorderSideWidth, BorderSpacing, LineWidth,
+};
+pub use self::box_::{
+ Appearance, BaselineSource, BreakBetween, BreakWithin, Clear, Contain, ContainIntrinsicSize,
+ ContainerName, ContainerType, ContentVisibility, Display, Float, LineClamp, Overflow,
+ OverflowAnchor, OverflowClipBox, OverscrollBehavior, Perspective, Resize, ScrollSnapAlign,
+ ScrollSnapAxis, ScrollSnapStop, ScrollSnapStrictness, ScrollSnapType, ScrollbarGutter,
+ TouchAction, VerticalAlign, WillChange, Zoom,
+};
+pub use self::color::{
+ Color, ColorOrAuto, ColorPropertyValue, ColorScheme, ForcedColorAdjust, PrintColorAdjust,
+};
+pub use self::column::ColumnCount;
+pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset, CounterSet};
+pub use self::easing::TimingFunction;
+pub use self::effects::{BoxShadow, Filter, SimpleShadow};
+pub use self::flex::FlexBasis;
+pub use self::font::{FontFamily, FontLanguageOverride, FontPalette, FontStyle};
+pub use self::font::{FontFeatureSettings, FontVariantLigatures, FontVariantNumeric};
+pub use self::font::{FontSize, FontSizeAdjust, FontStretch, FontSynthesis, LineHeight};
+pub use self::font::{FontVariantAlternates, FontWeight};
+pub use self::font::{FontVariantEastAsian, FontVariationSettings};
+pub use self::font::{MathDepth, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextScale};
+pub use self::image::{Gradient, Image, ImageRendering, LineDirection};
+pub use self::length::{CSSPixelLength, NonNegativeLength};
+pub use self::length::{Length, LengthOrNumber, LengthPercentage, NonNegativeLengthOrNumber};
+pub use self::length::{LengthOrAuto, LengthPercentageOrAuto, MaxSize, Size};
+pub use self::length::{NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto};
+#[cfg(feature = "gecko")]
+pub use self::list::ListStyleType;
+pub use self::list::Quotes;
+pub use self::motion::{OffsetPath, OffsetPosition, OffsetRotate};
+pub use self::outline::OutlineStyle;
+pub use self::page::{PageName, PageOrientation, PageSize, PageSizeOrientation, PaperSize};
+pub use self::percentage::{NonNegativePercentage, Percentage};
+pub use self::position::AspectRatio;
+pub use self::position::{
+ GridAutoFlow, GridTemplateAreas, MasonryAutoFlow, Position, PositionOrAuto, ZIndex,
+};
+pub use self::ratio::Ratio;
+pub use self::rect::NonNegativeLengthOrNumberRect;
+pub use self::resolution::Resolution;
+pub use self::svg::{DProperty, MozContextProperties};
+pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind};
+pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth};
+pub use self::text::HyphenateCharacter;
+pub use self::text::TextUnderlinePosition;
+pub use self::text::{InitialLetter, LetterSpacing, LineBreak, TextIndent};
+pub use self::text::{OverflowWrap, RubyPosition, TextOverflow, WordBreak, WordSpacing};
+pub use self::text::{TextAlign, TextAlignLast, TextEmphasisPosition, TextEmphasisStyle};
+pub use self::text::{TextDecorationLength, TextDecorationSkipInk, TextJustify};
+pub use self::time::Time;
+pub use self::transform::{Rotate, Scale, Transform, TransformBox, TransformOperation};
+pub use self::transform::{TransformOrigin, TransformStyle, Translate};
+#[cfg(feature = "gecko")]
+pub use self::ui::CursorImage;
+pub use self::ui::{BoolInteger, Cursor, UserSelect};
+pub use super::specified::TextTransform;
+pub use super::specified::ViewportVariant;
+pub use super::specified::{BorderStyle, TextDecorationLine};
+pub use app_units::Au;
+
+#[cfg(feature = "gecko")]
+pub mod align;
+pub mod angle;
+pub mod animation;
+pub mod background;
+pub mod basic_shape;
+pub mod border;
+#[path = "box.rs"]
+pub mod box_;
+pub mod color;
+pub mod column;
+pub mod counters;
+pub mod easing;
+pub mod effects;
+pub mod flex;
+pub mod font;
+pub mod image;
+pub mod length;
+pub mod length_percentage;
+pub mod list;
+pub mod motion;
+pub mod outline;
+pub mod page;
+pub mod percentage;
+pub mod position;
+pub mod ratio;
+pub mod rect;
+pub mod resolution;
+pub mod svg;
+pub mod table;
+pub mod text;
+pub mod time;
+pub mod transform;
+pub mod ui;
+pub mod url;
+
+/// A `Context` is all the data a specified value could ever need to compute
+/// itself and be transformed to a computed value.
+pub struct Context<'a> {
+ /// Values accessed through this need to be in the properties "computed
+ /// early": color, text-decoration, font-size, display, position, float,
+ /// border-*-style, outline-style, font-family, writing-mode...
+ pub builder: StyleBuilder<'a>,
+
+ /// A cached computed system font value, for use by gecko.
+ ///
+ /// See properties/longhands/font.mako.rs
+ #[cfg(feature = "gecko")]
+ pub cached_system_font: Option<properties::longhands::system_font::ComputedSystemFont>,
+
+ /// A dummy option for servo so initializing a computed::Context isn't
+ /// painful.
+ ///
+ /// TODO(emilio): Make constructors for Context, and drop this.
+ #[cfg(feature = "servo")]
+ pub cached_system_font: Option<()>,
+
+ /// Whether or not we are computing the media list in a media query.
+ pub in_media_query: bool,
+
+ /// Whether or not we are computing the container query condition.
+ pub in_container_query: bool,
+
+ /// The quirks mode of this context.
+ pub quirks_mode: QuirksMode,
+
+ /// Whether this computation is being done for a SMIL animation.
+ ///
+ /// This is used to allow certain properties to generate out-of-range
+ /// values, which SMIL allows.
+ pub for_smil_animation: bool,
+
+ /// Returns the container information to evaluate a given container query.
+ pub container_info: Option<ContainerInfo>,
+
+ /// Whether we're computing a value for a non-inherited property.
+ /// False if we are computed a value for an inherited property or not computing for a property
+ /// at all (e.g. in a media query evaluation).
+ pub for_non_inherited_property: bool,
+
+ /// The conditions to cache a rule node on the rule cache.
+ ///
+ /// FIXME(emilio): Drop the refcell.
+ pub rule_cache_conditions: RefCell<&'a mut RuleCacheConditions>,
+
+ /// Container size query for this context.
+ container_size_query: RefCell<ContainerSizeQuery<'a>>,
+}
+
+impl<'a> Context<'a> {
+ /// Lazily evaluate the container size query, returning the result.
+ pub fn get_container_size_query(&self) -> ContainerSizeQueryResult {
+ let mut resolved = self.container_size_query.borrow_mut();
+ resolved.get().clone()
+ }
+
+ /// Creates a suitable context for media query evaluation, in which
+ /// font-relative units compute against the system_font, and executes `f`
+ /// with it.
+ pub fn for_media_query_evaluation<F, R>(device: &Device, quirks_mode: QuirksMode, f: F) -> R
+ where
+ F: FnOnce(&Context) -> R,
+ {
+ let mut conditions = RuleCacheConditions::default();
+ let context = Context {
+ builder: StyleBuilder::for_inheritance(device, None, None, None),
+ cached_system_font: None,
+ in_media_query: true,
+ in_container_query: false,
+ quirks_mode,
+ for_smil_animation: false,
+ container_info: None,
+ for_non_inherited_property: false,
+ rule_cache_conditions: RefCell::new(&mut conditions),
+ container_size_query: RefCell::new(ContainerSizeQuery::none()),
+ };
+ f(&context)
+ }
+
+ /// Creates a suitable context for container query evaluation for the style
+ /// specified.
+ pub fn for_container_query_evaluation<F, R>(
+ device: &Device,
+ stylist: Option<&Stylist>,
+ container_info_and_style: Option<(ContainerInfo, Arc<ComputedValues>)>,
+ container_size_query: ContainerSizeQuery,
+ f: F,
+ ) -> R
+ where
+ F: FnOnce(&Context) -> R,
+ {
+ let mut conditions = RuleCacheConditions::default();
+
+ let (container_info, style) = match container_info_and_style {
+ Some((ci, s)) => (Some(ci), Some(s)),
+ None => (None, None),
+ };
+
+ let style = style.as_ref().map(|s| &**s);
+ let quirks_mode = device.quirks_mode();
+ let context = Context {
+ builder: StyleBuilder::for_inheritance(device, stylist, style, None),
+ cached_system_font: None,
+ in_media_query: false,
+ in_container_query: true,
+ quirks_mode,
+ for_smil_animation: false,
+ container_info,
+ for_non_inherited_property: false,
+ rule_cache_conditions: RefCell::new(&mut conditions),
+ container_size_query: RefCell::new(container_size_query),
+ };
+
+ f(&context)
+ }
+
+ /// Creates a context suitable for more general cases.
+ pub fn new(
+ builder: StyleBuilder<'a>,
+ quirks_mode: QuirksMode,
+ rule_cache_conditions: &'a mut RuleCacheConditions,
+ container_size_query: ContainerSizeQuery<'a>,
+ ) -> Self {
+ Self {
+ builder,
+ cached_system_font: None,
+ in_media_query: false,
+ in_container_query: false,
+ quirks_mode,
+ container_info: None,
+ for_smil_animation: false,
+ for_non_inherited_property: false,
+ rule_cache_conditions: RefCell::new(rule_cache_conditions),
+ container_size_query: RefCell::new(container_size_query),
+ }
+ }
+
+ /// Creates a context suitable for computing animations.
+ pub fn new_for_animation(
+ builder: StyleBuilder<'a>,
+ for_smil_animation: bool,
+ quirks_mode: QuirksMode,
+ rule_cache_conditions: &'a mut RuleCacheConditions,
+ container_size_query: ContainerSizeQuery<'a>,
+ ) -> Self {
+ Self {
+ builder,
+ cached_system_font: None,
+ in_media_query: false,
+ in_container_query: false,
+ quirks_mode,
+ container_info: None,
+ for_smil_animation,
+ for_non_inherited_property: false,
+ rule_cache_conditions: RefCell::new(rule_cache_conditions),
+ container_size_query: RefCell::new(container_size_query),
+ }
+ }
+
+ /// Creates a context suitable for computing the initial value of @property.
+ pub fn new_for_initial_at_property_value(
+ stylist: &'a Stylist,
+ rule_cache_conditions: &'a mut RuleCacheConditions,
+ ) -> Self {
+ Self {
+ builder: StyleBuilder::new(stylist.device(), Some(stylist), None, None, None, false),
+ cached_system_font: None,
+ // Because font-relative values are disallowed in @property initial values, we do not
+ // need to keep track of whether we're in a media query, whether we're in a container
+ // query, and so on.
+ in_media_query: false,
+ in_container_query: false,
+ quirks_mode: stylist.quirks_mode(),
+ container_info: None,
+ for_smil_animation: false,
+ for_non_inherited_property: false,
+ rule_cache_conditions: RefCell::new(rule_cache_conditions),
+ container_size_query: RefCell::new(ContainerSizeQuery::none()),
+ }
+ }
+
+ /// The current device.
+ pub fn device(&self) -> &Device {
+ self.builder.device
+ }
+
+ /// Get the inherited custom properties map.
+ pub fn inherited_custom_properties(&self) -> &ComputedCustomProperties {
+ &self.builder.inherited_custom_properties()
+ }
+
+ /// Whether the style is for the root element.
+ pub fn is_root_element(&self) -> bool {
+ self.builder.is_root_element
+ }
+
+ /// Queries font metrics.
+ pub fn query_font_metrics(
+ &self,
+ base_size: FontBaseSize,
+ orientation: FontMetricsOrientation,
+ retrieve_math_scales: bool,
+ ) -> FontMetrics {
+ if self.for_non_inherited_property {
+ self.rule_cache_conditions.borrow_mut().set_uncacheable();
+ }
+ self.builder.add_flags(match base_size {
+ FontBaseSize::CurrentStyle => ComputedValueFlags::DEPENDS_ON_SELF_FONT_METRICS,
+ FontBaseSize::InheritedStyle => ComputedValueFlags::DEPENDS_ON_INHERITED_FONT_METRICS,
+ });
+ let size = base_size.resolve(self).used_size();
+ let style = self.style();
+
+ let (wm, font) = match base_size {
+ FontBaseSize::CurrentStyle => (style.writing_mode, style.get_font()),
+ // This is only used for font-size computation.
+ FontBaseSize::InheritedStyle => {
+ (*style.inherited_writing_mode(), style.get_parent_font())
+ },
+ };
+
+ let vertical = match orientation {
+ FontMetricsOrientation::MatchContextPreferHorizontal => {
+ wm.is_vertical() && wm.is_upright()
+ },
+ FontMetricsOrientation::MatchContextPreferVertical => wm.is_text_vertical(),
+ FontMetricsOrientation::Horizontal => false,
+ };
+ self.device().query_font_metrics(
+ vertical,
+ font,
+ size,
+ self.in_media_or_container_query(),
+ retrieve_math_scales,
+ )
+ }
+
+ /// The current viewport size, used to resolve viewport units.
+ pub fn viewport_size_for_viewport_unit_resolution(
+ &self,
+ variant: ViewportVariant,
+ ) -> default::Size2D<Au> {
+ self.builder
+ .add_flags(ComputedValueFlags::USES_VIEWPORT_UNITS);
+ self.builder
+ .device
+ .au_viewport_size_for_viewport_unit_resolution(variant)
+ }
+
+ /// Whether we're in a media or container query.
+ pub fn in_media_or_container_query(&self) -> bool {
+ self.in_media_query || self.in_container_query
+ }
+
+ /// The default computed style we're getting our reset style from.
+ pub fn default_style(&self) -> &ComputedValues {
+ self.builder.default_style()
+ }
+
+ /// The current style.
+ pub fn style(&self) -> &StyleBuilder {
+ &self.builder
+ }
+
+ /// Apply text-zoom if enabled.
+ #[cfg(feature = "gecko")]
+ pub fn maybe_zoom_text(&self, size: CSSPixelLength) -> CSSPixelLength {
+ if self
+ .style()
+ .get_font()
+ .clone__x_text_scale()
+ .text_zoom_enabled()
+ {
+ self.device().zoom_text(size)
+ } else {
+ size
+ }
+ }
+
+ /// (Servo doesn't do text-zoom)
+ #[cfg(feature = "servo")]
+ pub fn maybe_zoom_text(&self, size: CSSPixelLength) -> CSSPixelLength {
+ size
+ }
+}
+
+/// An iterator over a slice of computed values
+#[derive(Clone)]
+pub struct ComputedVecIter<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> {
+ cx: &'cx Context<'cx_a>,
+ values: &'a [S],
+}
+
+impl<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> ComputedVecIter<'a, 'cx, 'cx_a, S> {
+ /// Construct an iterator from a slice of specified values and a context
+ pub fn new(cx: &'cx Context<'cx_a>, values: &'a [S]) -> Self {
+ ComputedVecIter { cx, values }
+ }
+}
+
+impl<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> ExactSizeIterator
+ for ComputedVecIter<'a, 'cx, 'cx_a, S>
+{
+ fn len(&self) -> usize {
+ self.values.len()
+ }
+}
+
+impl<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> Iterator for ComputedVecIter<'a, 'cx, 'cx_a, S> {
+ type Item = S::ComputedValue;
+ fn next(&mut self) -> Option<Self::Item> {
+ if let Some((next, rest)) = self.values.split_first() {
+ let ret = next.to_computed_value(self.cx);
+ self.values = rest;
+ Some(ret)
+ } else {
+ None
+ }
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ (self.values.len(), Some(self.values.len()))
+ }
+}
+
+/// A trait to represent the conversion between computed and specified values.
+///
+/// This trait is derivable with `#[derive(ToComputedValue)]`. The derived
+/// implementation just calls `ToComputedValue::to_computed_value` on each field
+/// of the passed value. The deriving code assumes that if the type isn't
+/// generic, then the trait can be implemented as simple `Clone::clone` calls,
+/// this means that a manual implementation with `ComputedValue = Self` is bogus
+/// if it returns anything else than a clone.
+pub trait ToComputedValue {
+ /// The computed value type we're going to be converted to.
+ type ComputedValue;
+
+ /// Convert a specified value to a computed value, using itself and the data
+ /// inside the `Context`.
+ fn to_computed_value(&self, context: &Context) -> Self::ComputedValue;
+
+ /// Convert a computed value to specified value form.
+ ///
+ /// This will be used for recascading during animation.
+ /// Such from_computed_valued values should recompute to the same value.
+ fn from_computed_value(computed: &Self::ComputedValue) -> Self;
+}
+
+impl<A, B> ToComputedValue for (A, B)
+where
+ A: ToComputedValue,
+ B: ToComputedValue,
+{
+ type ComputedValue = (
+ <A as ToComputedValue>::ComputedValue,
+ <B as ToComputedValue>::ComputedValue,
+ );
+
+ #[inline]
+ fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+ (
+ self.0.to_computed_value(context),
+ self.1.to_computed_value(context),
+ )
+ }
+
+ #[inline]
+ fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+ (
+ A::from_computed_value(&computed.0),
+ B::from_computed_value(&computed.1),
+ )
+ }
+}
+
+impl<T> ToComputedValue for Option<T>
+where
+ T: ToComputedValue,
+{
+ type ComputedValue = Option<<T as ToComputedValue>::ComputedValue>;
+
+ #[inline]
+ fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+ self.as_ref().map(|item| item.to_computed_value(context))
+ }
+
+ #[inline]
+ fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+ computed.as_ref().map(T::from_computed_value)
+ }
+}
+
+impl<T> ToComputedValue for default::Size2D<T>
+where
+ T: ToComputedValue,
+{
+ type ComputedValue = default::Size2D<<T as ToComputedValue>::ComputedValue>;
+
+ #[inline]
+ fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+ Size2D::new(
+ self.width.to_computed_value(context),
+ self.height.to_computed_value(context),
+ )
+ }
+
+ #[inline]
+ fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+ Size2D::new(
+ T::from_computed_value(&computed.width),
+ T::from_computed_value(&computed.height),
+ )
+ }
+}
+
+impl<T> ToComputedValue for Vec<T>
+where
+ T: ToComputedValue,
+{
+ type ComputedValue = Vec<<T as ToComputedValue>::ComputedValue>;
+
+ #[inline]
+ fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+ self.iter()
+ .map(|item| item.to_computed_value(context))
+ .collect()
+ }
+
+ #[inline]
+ fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+ computed.iter().map(T::from_computed_value).collect()
+ }
+}
+
+impl<T> ToComputedValue for Box<T>
+where
+ T: ToComputedValue,
+{
+ type ComputedValue = Box<<T as ToComputedValue>::ComputedValue>;
+
+ #[inline]
+ fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+ Box::new(T::to_computed_value(self, context))
+ }
+
+ #[inline]
+ fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+ Box::new(T::from_computed_value(computed))
+ }
+}
+
+impl<T> ToComputedValue for Box<[T]>
+where
+ T: ToComputedValue,
+{
+ type ComputedValue = Box<[<T as ToComputedValue>::ComputedValue]>;
+
+ #[inline]
+ fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+ self.iter()
+ .map(|item| item.to_computed_value(context))
+ .collect::<Vec<_>>()
+ .into_boxed_slice()
+ }
+
+ #[inline]
+ fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+ computed
+ .iter()
+ .map(T::from_computed_value)
+ .collect::<Vec<_>>()
+ .into_boxed_slice()
+ }
+}
+
+impl<T> ToComputedValue for crate::OwnedSlice<T>
+where
+ T: ToComputedValue,
+{
+ type ComputedValue = crate::OwnedSlice<<T as ToComputedValue>::ComputedValue>;
+
+ #[inline]
+ fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+ self.iter()
+ .map(|item| item.to_computed_value(context))
+ .collect()
+ }
+
+ #[inline]
+ fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+ computed.iter().map(T::from_computed_value).collect()
+ }
+}
+
+// NOTE(emilio): This is implementable more generically, but it's unlikely
+// what you want there, as it forces you to have an extra allocation.
+//
+// We could do that if needed, ideally with specialization for the case where
+// ComputedValue = T. But we don't need it for now.
+impl<T> ToComputedValue for Arc<T>
+where
+ T: ToComputedValue<ComputedValue = T>,
+{
+ type ComputedValue = Self;
+
+ #[inline]
+ fn to_computed_value(&self, _: &Context) -> Self {
+ self.clone()
+ }
+
+ #[inline]
+ fn from_computed_value(computed: &Self) -> Self {
+ computed.clone()
+ }
+}
+
+// Same caveat as above applies.
+impl<T> ToComputedValue for ArcSlice<T>
+where
+ T: ToComputedValue<ComputedValue = T>,
+{
+ type ComputedValue = Self;
+
+ #[inline]
+ fn to_computed_value(&self, _: &Context) -> Self {
+ self.clone()
+ }
+
+ #[inline]
+ fn from_computed_value(computed: &Self) -> Self {
+ computed.clone()
+ }
+}
+
+trivial_to_computed_value!(());
+trivial_to_computed_value!(bool);
+trivial_to_computed_value!(f32);
+trivial_to_computed_value!(i32);
+trivial_to_computed_value!(u8);
+trivial_to_computed_value!(u16);
+trivial_to_computed_value!(u32);
+trivial_to_computed_value!(usize);
+trivial_to_computed_value!(Atom);
+trivial_to_computed_value!(crate::values::AtomIdent);
+#[cfg(feature = "servo")]
+trivial_to_computed_value!(crate::Namespace);
+#[cfg(feature = "servo")]
+trivial_to_computed_value!(crate::Prefix);
+trivial_to_computed_value!(String);
+trivial_to_computed_value!(Box<str>);
+trivial_to_computed_value!(crate::OwnedStr);
+trivial_to_computed_value!(style_traits::values::specified::AllowedNumericType);
+trivial_to_computed_value!(crate::values::generics::color::ColorMixFlags);
+
+#[allow(missing_docs)]
+#[derive(
+ Animate,
+ Clone,
+ ComputeSquaredDistance,
+ Copy,
+ Debug,
+ MallocSizeOf,
+ PartialEq,
+ ToAnimatedZero,
+ ToCss,
+ ToResolvedValue,
+)]
+#[repr(C, u8)]
+pub enum AngleOrPercentage {
+ Percentage(Percentage),
+ Angle(Angle),
+}
+
+impl ToComputedValue for specified::AngleOrPercentage {
+ type ComputedValue = AngleOrPercentage;
+
+ #[inline]
+ fn to_computed_value(&self, context: &Context) -> AngleOrPercentage {
+ match *self {
+ specified::AngleOrPercentage::Percentage(percentage) => {
+ AngleOrPercentage::Percentage(percentage.to_computed_value(context))
+ },
+ specified::AngleOrPercentage::Angle(angle) => {
+ AngleOrPercentage::Angle(angle.to_computed_value(context))
+ },
+ }
+ }
+ #[inline]
+ fn from_computed_value(computed: &AngleOrPercentage) -> Self {
+ match *computed {
+ AngleOrPercentage::Percentage(percentage) => specified::AngleOrPercentage::Percentage(
+ ToComputedValue::from_computed_value(&percentage),
+ ),
+ AngleOrPercentage::Angle(angle) => {
+ specified::AngleOrPercentage::Angle(ToComputedValue::from_computed_value(&angle))
+ },
+ }
+ }
+}
+
+/// A `<number>` value.
+pub type Number = CSSFloat;
+
+impl IsParallelTo for (Number, Number, Number) {
+ fn is_parallel_to(&self, vector: &DirectionVector) -> bool {
+ use euclid::approxeq::ApproxEq;
+ // If a and b is parallel, the angle between them is 0deg, so
+ // a x b = |a|*|b|*sin(0)*n = 0 * n, |a x b| == 0.
+ let self_vector = DirectionVector::new(self.0, self.1, self.2);
+ self_vector
+ .cross(*vector)
+ .square_length()
+ .approx_eq(&0.0f32)
+ }
+}
+
+/// A wrapper of Number, but the value >= 0.
+pub type NonNegativeNumber = NonNegative<CSSFloat>;
+
+impl ToAnimatedValue for NonNegativeNumber {
+ type AnimatedValue = CSSFloat;
+
+ #[inline]
+ fn to_animated_value(self) -> Self::AnimatedValue {
+ self.0
+ }
+
+ #[inline]
+ fn from_animated_value(animated: Self::AnimatedValue) -> Self {
+ animated.max(0.).into()
+ }
+}
+
+impl From<CSSFloat> for NonNegativeNumber {
+ #[inline]
+ fn from(number: CSSFloat) -> NonNegativeNumber {
+ NonNegative::<CSSFloat>(number)
+ }
+}
+
+impl From<NonNegativeNumber> for CSSFloat {
+ #[inline]
+ fn from(number: NonNegativeNumber) -> CSSFloat {
+ number.0
+ }
+}
+
+impl One for NonNegativeNumber {
+ #[inline]
+ fn one() -> Self {
+ NonNegative(1.0)
+ }
+
+ #[inline]
+ fn is_one(&self) -> bool {
+ self.0 == 1.0
+ }
+}
+
+/// A wrapper of Number, but the value between 0 and 1
+pub type ZeroToOneNumber = ZeroToOne<CSSFloat>;
+
+impl ToAnimatedValue for ZeroToOneNumber {
+ type AnimatedValue = CSSFloat;
+
+ #[inline]
+ fn to_animated_value(self) -> Self::AnimatedValue {
+ self.0
+ }
+
+ #[inline]
+ fn from_animated_value(animated: Self::AnimatedValue) -> Self {
+ Self(animated.max(0.).min(1.))
+ }
+}
+
+impl From<CSSFloat> for ZeroToOneNumber {
+ #[inline]
+ fn from(number: CSSFloat) -> Self {
+ Self(number)
+ }
+}
+
+/// A wrapper of Number, but the value >= 1.
+pub type GreaterThanOrEqualToOneNumber = GreaterThanOrEqualToOne<CSSFloat>;
+
+impl ToAnimatedValue for GreaterThanOrEqualToOneNumber {
+ type AnimatedValue = CSSFloat;
+
+ #[inline]
+ fn to_animated_value(self) -> Self::AnimatedValue {
+ self.0
+ }
+
+ #[inline]
+ fn from_animated_value(animated: Self::AnimatedValue) -> Self {
+ animated.max(1.).into()
+ }
+}
+
+impl From<CSSFloat> for GreaterThanOrEqualToOneNumber {
+ #[inline]
+ fn from(number: CSSFloat) -> GreaterThanOrEqualToOneNumber {
+ GreaterThanOrEqualToOne::<CSSFloat>(number)
+ }
+}
+
+impl From<GreaterThanOrEqualToOneNumber> for CSSFloat {
+ #[inline]
+ fn from(number: GreaterThanOrEqualToOneNumber) -> CSSFloat {
+ number.0
+ }
+}
+
+#[allow(missing_docs)]
+#[derive(
+ Animate,
+ Clone,
+ ComputeSquaredDistance,
+ Copy,
+ Debug,
+ MallocSizeOf,
+ PartialEq,
+ ToAnimatedZero,
+ ToCss,
+ ToResolvedValue,
+)]
+#[repr(C, u8)]
+pub enum NumberOrPercentage {
+ Percentage(Percentage),
+ Number(Number),
+}
+
+impl NumberOrPercentage {
+ fn clamp_to_non_negative(self) -> Self {
+ match self {
+ NumberOrPercentage::Percentage(p) => {
+ NumberOrPercentage::Percentage(p.clamp_to_non_negative())
+ },
+ NumberOrPercentage::Number(n) => NumberOrPercentage::Number(n.max(0.)),
+ }
+ }
+}
+
+impl ToComputedValue for specified::NumberOrPercentage {
+ type ComputedValue = NumberOrPercentage;
+
+ #[inline]
+ fn to_computed_value(&self, context: &Context) -> NumberOrPercentage {
+ match *self {
+ specified::NumberOrPercentage::Percentage(percentage) => {
+ NumberOrPercentage::Percentage(percentage.to_computed_value(context))
+ },
+ specified::NumberOrPercentage::Number(number) => {
+ NumberOrPercentage::Number(number.to_computed_value(context))
+ },
+ }
+ }
+ #[inline]
+ fn from_computed_value(computed: &NumberOrPercentage) -> Self {
+ match *computed {
+ NumberOrPercentage::Percentage(percentage) => {
+ specified::NumberOrPercentage::Percentage(ToComputedValue::from_computed_value(
+ &percentage,
+ ))
+ },
+ NumberOrPercentage::Number(number) => {
+ specified::NumberOrPercentage::Number(ToComputedValue::from_computed_value(&number))
+ },
+ }
+ }
+}
+
+/// A non-negative <number-percentage>.
+pub type NonNegativeNumberOrPercentage = NonNegative<NumberOrPercentage>;
+
+impl NonNegativeNumberOrPercentage {
+ /// Returns the `100%` value.
+ #[inline]
+ pub fn hundred_percent() -> Self {
+ NonNegative(NumberOrPercentage::Percentage(Percentage::hundred()))
+ }
+}
+
+impl ToAnimatedValue for NonNegativeNumberOrPercentage {
+ type AnimatedValue = NumberOrPercentage;
+
+ #[inline]
+ fn to_animated_value(self) -> Self::AnimatedValue {
+ self.0
+ }
+
+ #[inline]
+ fn from_animated_value(animated: Self::AnimatedValue) -> Self {
+ NonNegative(animated.clamp_to_non_negative())
+ }
+}
+
+/// A type used for opacity.
+pub type Opacity = CSSFloat;
+
+/// A `<integer>` value.
+pub type Integer = CSSInteger;
+
+/// A wrapper of Integer, but only accept a value >= 1.
+pub type PositiveInteger = GreaterThanOrEqualToOne<CSSInteger>;
+
+impl ToAnimatedValue for PositiveInteger {
+ type AnimatedValue = CSSInteger;
+
+ #[inline]
+ fn to_animated_value(self) -> Self::AnimatedValue {
+ self.0
+ }
+
+ #[inline]
+ fn from_animated_value(animated: Self::AnimatedValue) -> Self {
+ cmp::max(animated, 1).into()
+ }
+}
+
+impl From<CSSInteger> for PositiveInteger {
+ #[inline]
+ fn from(int: CSSInteger) -> PositiveInteger {
+ GreaterThanOrEqualToOne::<CSSInteger>(int)
+ }
+}
+
+/// rect(...) | auto
+pub type ClipRect = generics::GenericClipRect<LengthOrAuto>;
+
+/// rect(...) | auto
+pub type ClipRectOrAuto = generics::GenericClipRectOrAuto<ClipRect>;
+
+/// The computed value of a grid `<track-breadth>`
+pub type TrackBreadth = GenericTrackBreadth<LengthPercentage>;
+
+/// The computed value of a grid `<track-size>`
+pub type TrackSize = GenericTrackSize<LengthPercentage>;
+
+/// The computed value of a grid `<track-size>+`
+pub type ImplicitGridTracks = GenericImplicitGridTracks<TrackSize>;
+
+/// The computed value of a grid `<track-list>`
+/// (could also be `<auto-track-list>` or `<explicit-track-list>`)
+pub type TrackList = GenericTrackList<LengthPercentage, Integer>;
+
+/// The computed value of a `<grid-line>`.
+pub type GridLine = GenericGridLine<Integer>;
+
+/// `<grid-template-rows> | <grid-template-columns>`
+pub type GridTemplateComponent = GenericGridTemplateComponent<LengthPercentage, Integer>;
+
+impl ClipRect {
+ /// Given a border box, resolves the clip rect against the border box
+ /// in the same space the border box is in
+ pub fn for_border_rect<T: Copy + From<Length> + Add<Output = T> + Sub<Output = T>, U>(
+ &self,
+ border_box: Rect<T, U>,
+ ) -> Rect<T, U> {
+ fn extract_clip_component<T: From<Length>>(p: &LengthOrAuto, or: T) -> T {
+ match *p {
+ LengthOrAuto::Auto => or,
+ LengthOrAuto::LengthPercentage(ref length) => T::from(*length),
+ }
+ }
+
+ let clip_origin = Point2D::new(
+ From::from(self.left.auto_is(|| Length::new(0.))),
+ From::from(self.top.auto_is(|| Length::new(0.))),
+ );
+ let right = extract_clip_component(&self.right, border_box.size.width);
+ let bottom = extract_clip_component(&self.bottom, border_box.size.height);
+ let clip_size = Size2D::new(right - clip_origin.x, bottom - clip_origin.y);
+
+ Rect::new(clip_origin, clip_size).translate(border_box.origin.to_vector())
+ }
+}
diff --git a/servo/components/style/values/computed/motion.rs b/servo/components/style/values/computed/motion.rs
new file mode 100644
index 0000000000..37b9f4909e
--- /dev/null
+++ b/servo/components/style/values/computed/motion.rs
@@ -0,0 +1,70 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Computed types for CSS values that are related to motion path.
+
+use crate::values::computed::basic_shape::BasicShape;
+use crate::values::computed::url::ComputedUrl;
+use crate::values::computed::{Angle, LengthPercentage, Position};
+use crate::values::generics::motion::{
+ GenericOffsetPath, GenericOffsetPathFunction, GenericOffsetPosition, GenericRayFunction,
+};
+use crate::Zero;
+
+/// The computed value of ray() function.
+pub type RayFunction = GenericRayFunction<Angle, Position>;
+
+/// The computed value of <offset-path>.
+pub type OffsetPathFunction = GenericOffsetPathFunction<BasicShape, RayFunction, ComputedUrl>;
+
+/// The computed value of `offset-path`.
+pub type OffsetPath = GenericOffsetPath<OffsetPathFunction>;
+
+/// The computed value of `offset-position`.
+pub type OffsetPosition = GenericOffsetPosition<LengthPercentage, LengthPercentage>;
+
+#[inline]
+fn is_auto_zero_angle(auto: &bool, angle: &Angle) -> bool {
+ *auto && angle.is_zero()
+}
+
+/// A computed offset-rotate.
+#[derive(
+ Animate,
+ Clone,
+ ComputeSquaredDistance,
+ Copy,
+ Debug,
+ Deserialize,
+ MallocSizeOf,
+ PartialEq,
+ Serialize,
+ ToAnimatedZero,
+ ToCss,
+ ToResolvedValue,
+)]
+#[repr(C)]
+pub struct OffsetRotate {
+ /// If auto is false, this is a fixed angle which indicates a
+ /// constant clockwise rotation transformation applied to it by this
+ /// specified rotation angle. Otherwise, the angle will be added to
+ /// the angle of the direction in layout.
+ #[animation(constant)]
+ #[css(represents_keyword)]
+ pub auto: bool,
+ /// The angle value.
+ #[css(contextual_skip_if = "is_auto_zero_angle")]
+ pub angle: Angle,
+}
+
+impl OffsetRotate {
+ /// Returns "auto 0deg".
+ #[inline]
+ pub fn auto() -> Self {
+ OffsetRotate {
+ auto: true,
+ angle: Zero::zero(),
+ }
+ }
+}
diff --git a/servo/components/style/values/computed/outline.rs b/servo/components/style/values/computed/outline.rs
new file mode 100644
index 0000000000..f872176529
--- /dev/null
+++ b/servo/components/style/values/computed/outline.rs
@@ -0,0 +1,7 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Computed values for outline properties
+
+pub use crate::values::specified::OutlineStyle;
diff --git a/servo/components/style/values/computed/page.rs b/servo/components/style/values/computed/page.rs
new file mode 100644
index 0000000000..6f71c912cf
--- /dev/null
+++ b/servo/components/style/values/computed/page.rs
@@ -0,0 +1,75 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Computed @page at-rule properties and named-page style properties
+
+use crate::values::computed::length::NonNegativeLength;
+use crate::values::computed::{Context, ToComputedValue};
+use crate::values::generics;
+use crate::values::generics::size::Size2D;
+
+use crate::values::specified::page as specified;
+pub use generics::page::GenericPageSize;
+pub use generics::page::PageOrientation;
+pub use generics::page::PageSizeOrientation;
+pub use generics::page::PaperSize;
+pub use specified::PageName;
+
+/// Computed value of the @page size descriptor
+///
+/// The spec says that the computed value should be the same as the specified
+/// value but with all absolute units, but it's not currently possibly observe
+/// the computed value of page-size.
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, ToShmem)]
+#[repr(C, u8)]
+pub enum PageSize {
+ /// Specified size, paper size, or paper size and orientation.
+ Size(Size2D<NonNegativeLength>),
+ /// `landscape` or `portrait` value, no specified size.
+ Orientation(PageSizeOrientation),
+ /// `auto` value
+ Auto,
+}
+
+impl ToComputedValue for specified::PageSize {
+ type ComputedValue = PageSize;
+
+ fn to_computed_value(&self, ctx: &Context) -> Self::ComputedValue {
+ match &*self {
+ Self::Size(s) => PageSize::Size(s.to_computed_value(ctx)),
+ Self::PaperSize(p, PageSizeOrientation::Landscape) => PageSize::Size(Size2D {
+ width: p.long_edge().to_computed_value(ctx),
+ height: p.short_edge().to_computed_value(ctx),
+ }),
+ Self::PaperSize(p, PageSizeOrientation::Portrait) => PageSize::Size(Size2D {
+ width: p.short_edge().to_computed_value(ctx),
+ height: p.long_edge().to_computed_value(ctx),
+ }),
+ Self::Orientation(o) => PageSize::Orientation(*o),
+ Self::Auto => PageSize::Auto,
+ }
+ }
+
+ fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+ match *computed {
+ PageSize::Size(s) => Self::Size(ToComputedValue::from_computed_value(&s)),
+ PageSize::Orientation(o) => Self::Orientation(o),
+ PageSize::Auto => Self::Auto,
+ }
+ }
+}
+
+impl PageSize {
+ /// `auto` value.
+ #[inline]
+ pub fn auto() -> Self {
+ PageSize::Auto
+ }
+
+ /// Whether this is the `auto` value.
+ #[inline]
+ pub fn is_auto(&self) -> bool {
+ matches!(*self, PageSize::Auto)
+ }
+}
diff --git a/servo/components/style/values/computed/percentage.rs b/servo/components/style/values/computed/percentage.rs
new file mode 100644
index 0000000000..994c01594a
--- /dev/null
+++ b/servo/components/style/values/computed/percentage.rs
@@ -0,0 +1,136 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Computed percentages.
+
+use crate::values::animated::ToAnimatedValue;
+use crate::values::generics::NonNegative;
+use crate::values::specified::percentage::ToPercentage;
+use crate::values::{serialize_normalized_percentage, CSSFloat};
+use crate::Zero;
+use std::fmt;
+use style_traits::{CssWriter, ToCss};
+
+/// A computed percentage.
+#[derive(
+ Animate,
+ Clone,
+ ComputeSquaredDistance,
+ Copy,
+ Debug,
+ Default,
+ Deserialize,
+ MallocSizeOf,
+ PartialEq,
+ PartialOrd,
+ Serialize,
+ SpecifiedValueInfo,
+ ToAnimatedValue,
+ ToAnimatedZero,
+ ToComputedValue,
+ ToResolvedValue,
+ ToShmem,
+)]
+#[repr(C)]
+pub struct Percentage(pub CSSFloat);
+
+impl Percentage {
+ /// 100%
+ #[inline]
+ pub fn hundred() -> Self {
+ Percentage(1.)
+ }
+
+ /// Returns the absolute value for this percentage.
+ #[inline]
+ pub fn abs(&self) -> Self {
+ Percentage(self.0.abs())
+ }
+
+ /// Clamps this percentage to a non-negative percentage.
+ #[inline]
+ pub fn clamp_to_non_negative(self) -> Self {
+ Percentage(self.0.max(0.))
+ }
+}
+
+impl Zero for Percentage {
+ fn zero() -> Self {
+ Percentage(0.)
+ }
+
+ fn is_zero(&self) -> bool {
+ self.0 == 0.
+ }
+}
+
+impl ToPercentage for Percentage {
+ fn to_percentage(&self) -> CSSFloat {
+ self.0
+ }
+}
+
+impl std::ops::AddAssign for Percentage {
+ fn add_assign(&mut self, other: Self) {
+ self.0 += other.0
+ }
+}
+
+impl std::ops::Add for Percentage {
+ type Output = Self;
+
+ fn add(self, other: Self) -> Self {
+ Percentage(self.0 + other.0)
+ }
+}
+
+impl std::ops::Sub for Percentage {
+ type Output = Self;
+
+ fn sub(self, other: Self) -> Self {
+ Percentage(self.0 - other.0)
+ }
+}
+
+impl std::ops::Rem for Percentage {
+ type Output = Self;
+
+ fn rem(self, other: Self) -> Self {
+ Percentage(self.0 % other.0)
+ }
+}
+
+impl ToCss for Percentage {
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: fmt::Write,
+ {
+ serialize_normalized_percentage(self.0, dest)
+ }
+}
+
+/// A wrapper over a `Percentage`, whose value should be clamped to 0.
+pub type NonNegativePercentage = NonNegative<Percentage>;
+
+impl NonNegativePercentage {
+ /// 100%
+ #[inline]
+ pub fn hundred() -> Self {
+ NonNegative(Percentage::hundred())
+ }
+}
+
+impl ToAnimatedValue for NonNegativePercentage {
+ type AnimatedValue = Percentage;
+
+ #[inline]
+ fn to_animated_value(self) -> Self::AnimatedValue {
+ self.0
+ }
+
+ #[inline]
+ fn from_animated_value(animated: Self::AnimatedValue) -> Self {
+ NonNegative(animated.clamp_to_non_negative())
+ }
+}
diff --git a/servo/components/style/values/computed/position.rs b/servo/components/style/values/computed/position.rs
new file mode 100644
index 0000000000..5a10c0f23d
--- /dev/null
+++ b/servo/components/style/values/computed/position.rs
@@ -0,0 +1,74 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! CSS handling for the computed value of
+//! [`position`][position] values.
+//!
+//! [position]: https://drafts.csswg.org/css-backgrounds-3/#position
+
+use crate::values::computed::{Integer, LengthPercentage, NonNegativeNumber, Percentage};
+use crate::values::generics::position::AspectRatio as GenericAspectRatio;
+use crate::values::generics::position::Position as GenericPosition;
+use crate::values::generics::position::PositionComponent as GenericPositionComponent;
+use crate::values::generics::position::PositionOrAuto as GenericPositionOrAuto;
+use crate::values::generics::position::ZIndex as GenericZIndex;
+pub use crate::values::specified::position::{GridAutoFlow, GridTemplateAreas, MasonryAutoFlow};
+use crate::Zero;
+use std::fmt::{self, Write};
+use style_traits::{CssWriter, ToCss};
+
+/// The computed value of a CSS `<position>`
+pub type Position = GenericPosition<HorizontalPosition, VerticalPosition>;
+
+/// The computed value of an `auto | <position>`
+pub type PositionOrAuto = GenericPositionOrAuto<Position>;
+
+/// The computed value of a CSS horizontal position.
+pub type HorizontalPosition = LengthPercentage;
+
+/// The computed value of a CSS vertical position.
+pub type VerticalPosition = LengthPercentage;
+
+impl Position {
+ /// `50% 50%`
+ #[inline]
+ pub fn center() -> Self {
+ Self::new(
+ LengthPercentage::new_percent(Percentage(0.5)),
+ LengthPercentage::new_percent(Percentage(0.5)),
+ )
+ }
+
+ /// `0% 0%`
+ #[inline]
+ pub fn zero() -> Self {
+ Self::new(LengthPercentage::zero(), LengthPercentage::zero())
+ }
+}
+
+impl ToCss for Position {
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: Write,
+ {
+ self.horizontal.to_css(dest)?;
+ dest.write_char(' ')?;
+ self.vertical.to_css(dest)
+ }
+}
+
+impl GenericPositionComponent for LengthPercentage {
+ fn is_center(&self) -> bool {
+ match self.to_percentage() {
+ Some(Percentage(per)) => per == 0.5,
+ _ => false,
+ }
+ }
+}
+
+/// A computed value for the `z-index` property.
+pub type ZIndex = GenericZIndex<Integer>;
+
+/// A computed value for the `aspect-ratio` property.
+pub type AspectRatio = GenericAspectRatio<NonNegativeNumber>;
diff --git a/servo/components/style/values/computed/ratio.rs b/servo/components/style/values/computed/ratio.rs
new file mode 100644
index 0000000000..ae8997cfc0
--- /dev/null
+++ b/servo/components/style/values/computed/ratio.rs
@@ -0,0 +1,115 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! `<ratio>` computed values.
+
+use crate::values::animated::{Animate, Procedure};
+use crate::values::computed::NonNegativeNumber;
+use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
+use crate::values::generics::ratio::Ratio as GenericRatio;
+use crate::{One, Zero};
+use std::cmp::{Ordering, PartialOrd};
+
+/// A computed <ratio> value.
+pub type Ratio = GenericRatio<NonNegativeNumber>;
+
+impl PartialOrd for Ratio {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ f64::partial_cmp(
+ &((self.0).0 as f64 * (other.1).0 as f64),
+ &((self.1).0 as f64 * (other.0).0 as f64),
+ )
+ }
+}
+
+/// https://drafts.csswg.org/css-values/#combine-ratio
+impl Animate for Ratio {
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+ // If either <ratio> is degenerate, the values cannot be interpolated.
+ if self.is_degenerate() || other.is_degenerate() {
+ return Err(());
+ }
+
+ // Addition of <ratio>s is not possible, and based on
+ // https://drafts.csswg.org/css-values-4/#not-additive,
+ // we simply use the first value as the result value.
+ // Besides, the procedure for accumulation should be identical to addition here.
+ if matches!(procedure, Procedure::Add | Procedure::Accumulate { .. }) {
+ return Ok(self.clone());
+ }
+
+ // The interpolation of a <ratio> is defined by converting each <ratio> to a number by
+ // dividing the first value by the second (so a ratio of 3 / 2 would become 1.5), taking
+ // the logarithm of that result (so the 1.5 would become approximately 0.176), then
+ // interpolating those values.
+ //
+ // The result during the interpolation is converted back to a <ratio> by inverting the
+ // logarithm, then interpreting the result as a <ratio> with the result as the first value
+ // and 1 as the second value.
+ let start = self.to_f32().ln();
+ let end = other.to_f32().ln();
+ let e = std::f32::consts::E;
+ let result = e.powf(start.animate(&end, procedure)?);
+ // The range of the result is [0, inf), based on the easing function.
+ if result.is_zero() || result.is_infinite() {
+ return Err(());
+ }
+ Ok(Ratio::new(result, 1.0f32))
+ }
+}
+
+impl ComputeSquaredDistance for Ratio {
+ fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
+ if self.is_degenerate() || other.is_degenerate() {
+ return Err(());
+ }
+ // Use the distance of their logarithm values. (This is used by testing, so don't need to
+ // care about the base. Here we use the same base as that in animate().)
+ self.to_f32()
+ .ln()
+ .compute_squared_distance(&other.to_f32().ln())
+ }
+}
+
+impl Zero for Ratio {
+ fn zero() -> Self {
+ Self::new(Zero::zero(), One::one())
+ }
+
+ fn is_zero(&self) -> bool {
+ self.0.is_zero()
+ }
+}
+
+impl Ratio {
+ /// Returns a new Ratio.
+ #[inline]
+ pub fn new(a: f32, b: f32) -> Self {
+ GenericRatio(a.into(), b.into())
+ }
+
+ /// Returns the used value. A ratio of 0/0 behaves as the ratio 1/0.
+ /// https://drafts.csswg.org/css-values-4/#ratios
+ pub fn used_value(self) -> Self {
+ if self.0.is_zero() && self.1.is_zero() {
+ Ratio::new(One::one(), Zero::zero())
+ } else {
+ self
+ }
+ }
+
+ /// Returns true if this is a degenerate ratio.
+ /// https://drafts.csswg.org/css-values/#degenerate-ratio
+ #[inline]
+ pub fn is_degenerate(&self) -> bool {
+ self.0.is_zero() || self.1.is_zero()
+ }
+
+ /// Returns the f32 value by dividing the first value by the second one.
+ #[inline]
+ fn to_f32(&self) -> f32 {
+ debug_assert!(!self.is_degenerate());
+ (self.0).0 / (self.1).0
+ }
+}
diff --git a/servo/components/style/values/computed/rect.rs b/servo/components/style/values/computed/rect.rs
new file mode 100644
index 0000000000..ec44360fc8
--- /dev/null
+++ b/servo/components/style/values/computed/rect.rs
@@ -0,0 +1,11 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Computed types for CSS borders.
+
+use crate::values::computed::length::NonNegativeLengthOrNumber;
+use crate::values::generics::rect::Rect;
+
+/// A specified rectangle made of four `<length-or-number>` values.
+pub type NonNegativeLengthOrNumberRect = Rect<NonNegativeLengthOrNumber>;
diff --git a/servo/components/style/values/computed/resolution.rs b/servo/components/style/values/computed/resolution.rs
new file mode 100644
index 0000000000..430c80d211
--- /dev/null
+++ b/servo/components/style/values/computed/resolution.rs
@@ -0,0 +1,56 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Resolution values:
+//!
+//! https://drafts.csswg.org/css-values/#resolution
+
+use crate::values::computed::{Context, ToComputedValue};
+use crate::values::specified;
+use crate::values::CSSFloat;
+use std::fmt::{self, Write};
+use style_traits::{CssWriter, ToCss};
+
+/// A computed `<resolution>`.
+#[repr(C)]
+#[derive(Animate, Clone, Debug, MallocSizeOf, PartialEq, ToResolvedValue, ToShmem)]
+pub struct Resolution(CSSFloat);
+
+impl Resolution {
+ /// Returns this resolution value as dppx.
+ #[inline]
+ pub fn dppx(&self) -> CSSFloat {
+ self.0
+ }
+
+ /// Return a computed `resolution` value from a dppx float value.
+ #[inline]
+ pub fn from_dppx(dppx: CSSFloat) -> Self {
+ Resolution(dppx)
+ }
+}
+
+impl ToComputedValue for specified::Resolution {
+ type ComputedValue = Resolution;
+
+ #[inline]
+ fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
+ Resolution(crate::values::normalize(self.dppx().max(0.0)))
+ }
+
+ #[inline]
+ fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+ specified::Resolution::from_dppx(computed.dppx())
+ }
+}
+
+impl ToCss for Resolution {
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: fmt::Write,
+ {
+ self.dppx().to_css(dest)?;
+ dest.write_str("dppx")
+ }
+}
diff --git a/servo/components/style/values/computed/svg.rs b/servo/components/style/values/computed/svg.rs
new file mode 100644
index 0000000000..6efdfca36b
--- /dev/null
+++ b/servo/components/style/values/computed/svg.rs
@@ -0,0 +1,66 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Computed types for SVG properties.
+
+use crate::values::computed::color::Color;
+use crate::values::computed::url::ComputedUrl;
+use crate::values::computed::{LengthPercentage, NonNegativeLengthPercentage, Opacity};
+use crate::values::generics::svg as generic;
+use crate::Zero;
+
+pub use crate::values::specified::{DProperty, MozContextProperties, SVGPaintOrder};
+
+/// Computed SVG Paint value
+pub type SVGPaint = generic::GenericSVGPaint<Color, ComputedUrl>;
+
+/// Computed SVG Paint Kind value
+pub type SVGPaintKind = generic::GenericSVGPaintKind<Color, ComputedUrl>;
+
+impl SVGPaint {
+ /// Opaque black color
+ pub const BLACK: Self = Self {
+ kind: generic::SVGPaintKind::Color(Color::BLACK),
+ fallback: generic::SVGPaintFallback::Unset,
+ };
+}
+
+/// <length> | <percentage> | <number> | context-value
+pub type SVGLength = generic::GenericSVGLength<LengthPercentage>;
+
+impl SVGLength {
+ /// `0px`
+ pub fn zero() -> Self {
+ generic::SVGLength::LengthPercentage(LengthPercentage::zero())
+ }
+}
+
+/// An non-negative wrapper of SVGLength.
+pub type SVGWidth = generic::GenericSVGLength<NonNegativeLengthPercentage>;
+
+impl SVGWidth {
+ /// `1px`.
+ pub fn one() -> Self {
+ use crate::values::generics::NonNegative;
+ generic::SVGLength::LengthPercentage(NonNegative(LengthPercentage::one()))
+ }
+}
+
+/// [ <length> | <percentage> | <number> ]# | context-value
+pub type SVGStrokeDashArray = generic::GenericSVGStrokeDashArray<NonNegativeLengthPercentage>;
+
+impl Default for SVGStrokeDashArray {
+ fn default() -> Self {
+ generic::SVGStrokeDashArray::Values(Default::default())
+ }
+}
+
+/// <opacity-value> | context-fill-opacity | context-stroke-opacity
+pub type SVGOpacity = generic::GenericSVGOpacity<Opacity>;
+
+impl Default for SVGOpacity {
+ fn default() -> Self {
+ generic::SVGOpacity::Opacity(1.)
+ }
+}
diff --git a/servo/components/style/values/computed/table.rs b/servo/components/style/values/computed/table.rs
new file mode 100644
index 0000000000..47109e20ec
--- /dev/null
+++ b/servo/components/style/values/computed/table.rs
@@ -0,0 +1,7 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Computed types for CSS values related to tables.
+
+pub use super::specified::table::CaptionSide;
diff --git a/servo/components/style/values/computed/text.rs b/servo/components/style/values/computed/text.rs
new file mode 100644
index 0000000000..a4fec654a5
--- /dev/null
+++ b/servo/components/style/values/computed/text.rs
@@ -0,0 +1,228 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Computed types for text properties.
+
+#[cfg(feature = "servo")]
+use crate::properties::StyleBuilder;
+use crate::values::computed::length::{Length, LengthPercentage};
+use crate::values::computed::{Context, ToComputedValue};
+use crate::values::generics::text::InitialLetter as GenericInitialLetter;
+use crate::values::generics::text::{GenericTextDecorationLength, GenericTextIndent, Spacing};
+use crate::values::specified::text::{self as specified, TextOverflowSide};
+use crate::values::specified::text::{TextEmphasisFillMode, TextEmphasisShapeKeyword};
+use crate::values::{CSSFloat, CSSInteger};
+use crate::Zero;
+use std::fmt::{self, Write};
+use style_traits::{CssWriter, ToCss};
+
+pub use crate::values::specified::text::{
+ MozControlCharacterVisibility, TextAlignLast, TextUnderlinePosition,
+};
+pub use crate::values::specified::HyphenateCharacter;
+pub use crate::values::specified::{LineBreak, OverflowWrap, RubyPosition, WordBreak};
+pub use crate::values::specified::{TextDecorationLine, TextEmphasisPosition};
+pub use crate::values::specified::{TextDecorationSkipInk, TextJustify, TextTransform};
+
+/// A computed value for the `initial-letter` property.
+pub type InitialLetter = GenericInitialLetter<CSSFloat, CSSInteger>;
+
+/// Implements type for `text-decoration-thickness` property.
+pub type TextDecorationLength = GenericTextDecorationLength<LengthPercentage>;
+
+/// The computed value of `text-align`.
+pub type TextAlign = specified::TextAlignKeyword;
+
+/// The computed value of `text-indent`.
+pub type TextIndent = GenericTextIndent<LengthPercentage>;
+
+/// A computed value for the `letter-spacing` property.
+#[repr(transparent)]
+#[derive(
+ Animate,
+ Clone,
+ ComputeSquaredDistance,
+ Copy,
+ Debug,
+ MallocSizeOf,
+ PartialEq,
+ ToAnimatedValue,
+ ToAnimatedZero,
+ ToResolvedValue,
+)]
+pub struct LetterSpacing(pub Length);
+
+impl LetterSpacing {
+ /// Return the `normal` computed value, which is just zero.
+ #[inline]
+ pub fn normal() -> Self {
+ LetterSpacing(Length::zero())
+ }
+}
+
+impl ToCss for LetterSpacing {
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: Write,
+ {
+ // https://drafts.csswg.org/css-text/#propdef-letter-spacing
+ //
+ // For legacy reasons, a computed letter-spacing of zero yields a
+ // resolved value (getComputedStyle() return value) of normal.
+ if self.0.is_zero() {
+ return dest.write_str("normal");
+ }
+ self.0.to_css(dest)
+ }
+}
+
+impl ToComputedValue for specified::LetterSpacing {
+ type ComputedValue = LetterSpacing;
+ fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+ match *self {
+ Spacing::Normal => LetterSpacing(Length::zero()),
+ Spacing::Value(ref v) => LetterSpacing(v.to_computed_value(context)),
+ }
+ }
+
+ fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+ if computed.0.is_zero() {
+ return Spacing::Normal;
+ }
+ Spacing::Value(ToComputedValue::from_computed_value(&computed.0))
+ }
+}
+
+/// A computed value for the `word-spacing` property.
+pub type WordSpacing = LengthPercentage;
+
+impl ToComputedValue for specified::WordSpacing {
+ type ComputedValue = WordSpacing;
+
+ fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+ match *self {
+ Spacing::Normal => LengthPercentage::zero(),
+ Spacing::Value(ref v) => v.to_computed_value(context),
+ }
+ }
+
+ fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+ Spacing::Value(ToComputedValue::from_computed_value(computed))
+ }
+}
+
+impl WordSpacing {
+ /// Return the `normal` computed value, which is just zero.
+ #[inline]
+ pub fn normal() -> Self {
+ LengthPercentage::zero()
+ }
+}
+
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToResolvedValue)]
+#[repr(C)]
+/// text-overflow.
+/// When the specified value only has one side, that's the "second"
+/// side, and the sides are logical, so "second" means "end". The
+/// start side is Clip in that case.
+///
+/// When the specified value has two sides, those are our "first"
+/// and "second" sides, and they are physical sides ("left" and
+/// "right").
+pub struct TextOverflow {
+ /// First side
+ pub first: TextOverflowSide,
+ /// Second side
+ pub second: TextOverflowSide,
+ /// True if the specified value only has one side.
+ pub sides_are_logical: bool,
+}
+
+impl TextOverflow {
+ /// Returns the initial `text-overflow` value
+ pub fn get_initial_value() -> TextOverflow {
+ TextOverflow {
+ first: TextOverflowSide::Clip,
+ second: TextOverflowSide::Clip,
+ sides_are_logical: true,
+ }
+ }
+}
+
+impl ToCss for TextOverflow {
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: Write,
+ {
+ if self.sides_are_logical {
+ debug_assert_eq!(self.first, TextOverflowSide::Clip);
+ self.second.to_css(dest)?;
+ } else {
+ self.first.to_css(dest)?;
+ dest.write_char(' ')?;
+ self.second.to_css(dest)?;
+ }
+ Ok(())
+ }
+}
+
+/// A struct that represents the _used_ value of the text-decoration property.
+///
+/// FIXME(emilio): This is done at style resolution time, though probably should
+/// be done at layout time, otherwise we need to account for display: contents
+/// and similar stuff when we implement it.
+///
+/// FIXME(emilio): Also, should be just a bitfield instead of three bytes.
+#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq, ToResolvedValue)]
+pub struct TextDecorationsInEffect {
+ /// Whether an underline is in effect.
+ pub underline: bool,
+ /// Whether an overline decoration is in effect.
+ pub overline: bool,
+ /// Whether a line-through style is in effect.
+ pub line_through: bool,
+}
+
+impl TextDecorationsInEffect {
+ /// Computes the text-decorations in effect for a given style.
+ #[cfg(feature = "servo")]
+ pub fn from_style(style: &StyleBuilder) -> Self {
+ // Start with no declarations if this is an atomic inline-level box;
+ // otherwise, start with the declarations in effect and add in the text
+ // decorations that this block specifies.
+ let mut result = if style.get_box().clone_display().is_atomic_inline_level() {
+ Self::default()
+ } else {
+ style
+ .get_parent_inherited_text()
+ .text_decorations_in_effect
+ .clone()
+ };
+
+ let line = style.get_text().clone_text_decoration_line();
+
+ result.underline |= line.contains(TextDecorationLine::UNDERLINE);
+ result.overline |= line.contains(TextDecorationLine::OVERLINE);
+ result.line_through |= line.contains(TextDecorationLine::LINE_THROUGH);
+
+ result
+ }
+}
+
+/// Computed value for the text-emphasis-style property
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue)]
+#[allow(missing_docs)]
+#[repr(C, u8)]
+pub enum TextEmphasisStyle {
+ /// [ <fill> || <shape> ]
+ Keyword {
+ #[css(skip_if = "TextEmphasisFillMode::is_filled")]
+ fill: TextEmphasisFillMode,
+ shape: TextEmphasisShapeKeyword,
+ },
+ /// `none`
+ None,
+ /// `<string>` (of which only the first grapheme cluster will be used).
+ String(crate::OwnedStr),
+}
diff --git a/servo/components/style/values/computed/time.rs b/servo/components/style/values/computed/time.rs
new file mode 100644
index 0000000000..b81c6e879a
--- /dev/null
+++ b/servo/components/style/values/computed/time.rs
@@ -0,0 +1,45 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Computed time values.
+
+use crate::values::CSSFloat;
+use std::fmt::{self, Write};
+use style_traits::{CssWriter, ToCss};
+
+/// A computed `<time>` value.
+#[derive(Animate, Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToResolvedValue)]
+#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+#[repr(C)]
+pub struct Time {
+ seconds: CSSFloat,
+}
+
+impl Time {
+ /// Creates a time value from a seconds amount.
+ pub fn from_seconds(seconds: CSSFloat) -> Self {
+ Time { seconds }
+ }
+
+ /// Returns `0s`.
+ pub fn zero() -> Self {
+ Self::from_seconds(0.0)
+ }
+
+ /// Returns the amount of seconds this time represents.
+ #[inline]
+ pub fn seconds(&self) -> CSSFloat {
+ self.seconds
+ }
+}
+
+impl ToCss for Time {
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: Write,
+ {
+ self.seconds().to_css(dest)?;
+ dest.write_char('s')
+ }
+}
diff --git a/servo/components/style/values/computed/transform.rs b/servo/components/style/values/computed/transform.rs
new file mode 100644
index 0000000000..b1a617fd4b
--- /dev/null
+++ b/servo/components/style/values/computed/transform.rs
@@ -0,0 +1,559 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Computed types for CSS values that are related to transformations.
+
+use super::CSSFloat;
+use crate::values::animated::transform::{Perspective, Scale3D, Translate3D};
+use crate::values::animated::ToAnimatedZero;
+use crate::values::computed::{Angle, Integer, Length, LengthPercentage, Number, Percentage};
+use crate::values::generics::transform as generic;
+use crate::Zero;
+use euclid::default::{Transform3D, Vector3D};
+
+pub use crate::values::generics::transform::TransformStyle;
+pub use crate::values::specified::transform::TransformBox;
+
+/// A single operation in a computed CSS `transform`
+pub type TransformOperation =
+ generic::GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>;
+/// A computed CSS `transform`
+pub type Transform = generic::GenericTransform<TransformOperation>;
+
+/// The computed value of a CSS `<transform-origin>`
+pub type TransformOrigin =
+ generic::GenericTransformOrigin<LengthPercentage, LengthPercentage, Length>;
+
+/// The computed value of the `perspective()` transform function.
+pub type PerspectiveFunction = generic::PerspectiveFunction<Length>;
+
+/// A vector to represent the direction vector (rotate axis) for Rotate3D.
+pub type DirectionVector = Vector3D<CSSFloat>;
+
+impl TransformOrigin {
+ /// Returns the initial computed value for `transform-origin`.
+ #[inline]
+ pub fn initial_value() -> Self {
+ Self::new(
+ LengthPercentage::new_percent(Percentage(0.5)),
+ LengthPercentage::new_percent(Percentage(0.5)),
+ Length::new(0.),
+ )
+ }
+}
+
+/// computed value of matrix3d()
+pub type Matrix3D = generic::Matrix3D<Number>;
+
+/// computed value of matrix()
+pub type Matrix = generic::Matrix<Number>;
+
+// we rustfmt_skip here because we want the matrices to look like
+// matrices instead of being split across lines
+#[cfg_attr(rustfmt, rustfmt_skip)]
+impl Matrix3D {
+ /// Get an identity matrix
+ #[inline]
+ pub fn identity() -> Self {
+ Self {
+ m11: 1.0, m12: 0.0, m13: 0.0, m14: 0.0,
+ m21: 0.0, m22: 1.0, m23: 0.0, m24: 0.0,
+ m31: 0.0, m32: 0.0, m33: 1.0, m34: 0.0,
+ m41: 0., m42: 0., m43: 0., m44: 1.0
+ }
+ }
+
+ /// Convert to a 2D Matrix
+ #[inline]
+ pub fn into_2d(self) -> Result<Matrix, ()> {
+ if self.m13 == 0. && self.m23 == 0. &&
+ self.m31 == 0. && self.m32 == 0. &&
+ self.m33 == 1. && self.m34 == 0. &&
+ self.m14 == 0. && self.m24 == 0. &&
+ self.m43 == 0. && self.m44 == 1. {
+ Ok(Matrix {
+ a: self.m11, c: self.m21, e: self.m41,
+ b: self.m12, d: self.m22, f: self.m42,
+ })
+ } else {
+ Err(())
+ }
+ }
+
+ /// Return true if this has 3D components.
+ #[inline]
+ pub fn is_3d(&self) -> bool {
+ self.m13 != 0.0 || self.m14 != 0.0 ||
+ self.m23 != 0.0 || self.m24 != 0.0 ||
+ self.m31 != 0.0 || self.m32 != 0.0 ||
+ self.m33 != 1.0 || self.m34 != 0.0 ||
+ self.m43 != 0.0 || self.m44 != 1.0
+ }
+
+ /// Return determinant value.
+ #[inline]
+ pub fn determinant(&self) -> CSSFloat {
+ self.m14 * self.m23 * self.m32 * self.m41 -
+ self.m13 * self.m24 * self.m32 * self.m41 -
+ self.m14 * self.m22 * self.m33 * self.m41 +
+ self.m12 * self.m24 * self.m33 * self.m41 +
+ self.m13 * self.m22 * self.m34 * self.m41 -
+ self.m12 * self.m23 * self.m34 * self.m41 -
+ self.m14 * self.m23 * self.m31 * self.m42 +
+ self.m13 * self.m24 * self.m31 * self.m42 +
+ self.m14 * self.m21 * self.m33 * self.m42 -
+ self.m11 * self.m24 * self.m33 * self.m42 -
+ self.m13 * self.m21 * self.m34 * self.m42 +
+ self.m11 * self.m23 * self.m34 * self.m42 +
+ self.m14 * self.m22 * self.m31 * self.m43 -
+ self.m12 * self.m24 * self.m31 * self.m43 -
+ self.m14 * self.m21 * self.m32 * self.m43 +
+ self.m11 * self.m24 * self.m32 * self.m43 +
+ self.m12 * self.m21 * self.m34 * self.m43 -
+ self.m11 * self.m22 * self.m34 * self.m43 -
+ self.m13 * self.m22 * self.m31 * self.m44 +
+ self.m12 * self.m23 * self.m31 * self.m44 +
+ self.m13 * self.m21 * self.m32 * self.m44 -
+ self.m11 * self.m23 * self.m32 * self.m44 -
+ self.m12 * self.m21 * self.m33 * self.m44 +
+ self.m11 * self.m22 * self.m33 * self.m44
+ }
+
+ /// Transpose a matrix.
+ #[inline]
+ pub fn transpose(&self) -> Self {
+ Self {
+ m11: self.m11, m12: self.m21, m13: self.m31, m14: self.m41,
+ m21: self.m12, m22: self.m22, m23: self.m32, m24: self.m42,
+ m31: self.m13, m32: self.m23, m33: self.m33, m34: self.m43,
+ m41: self.m14, m42: self.m24, m43: self.m34, m44: self.m44,
+ }
+ }
+
+ /// Return inverse matrix.
+ pub fn inverse(&self) -> Result<Matrix3D, ()> {
+ let mut det = self.determinant();
+
+ if det == 0.0 {
+ return Err(());
+ }
+
+ det = 1.0 / det;
+ let x = Matrix3D {
+ m11: det *
+ (self.m23 * self.m34 * self.m42 - self.m24 * self.m33 * self.m42 +
+ self.m24 * self.m32 * self.m43 - self.m22 * self.m34 * self.m43 -
+ self.m23 * self.m32 * self.m44 + self.m22 * self.m33 * self.m44),
+ m12: det *
+ (self.m14 * self.m33 * self.m42 - self.m13 * self.m34 * self.m42 -
+ self.m14 * self.m32 * self.m43 + self.m12 * self.m34 * self.m43 +
+ self.m13 * self.m32 * self.m44 - self.m12 * self.m33 * self.m44),
+ m13: det *
+ (self.m13 * self.m24 * self.m42 - self.m14 * self.m23 * self.m42 +
+ self.m14 * self.m22 * self.m43 - self.m12 * self.m24 * self.m43 -
+ self.m13 * self.m22 * self.m44 + self.m12 * self.m23 * self.m44),
+ m14: det *
+ (self.m14 * self.m23 * self.m32 - self.m13 * self.m24 * self.m32 -
+ self.m14 * self.m22 * self.m33 + self.m12 * self.m24 * self.m33 +
+ self.m13 * self.m22 * self.m34 - self.m12 * self.m23 * self.m34),
+ m21: det *
+ (self.m24 * self.m33 * self.m41 - self.m23 * self.m34 * self.m41 -
+ self.m24 * self.m31 * self.m43 + self.m21 * self.m34 * self.m43 +
+ self.m23 * self.m31 * self.m44 - self.m21 * self.m33 * self.m44),
+ m22: det *
+ (self.m13 * self.m34 * self.m41 - self.m14 * self.m33 * self.m41 +
+ self.m14 * self.m31 * self.m43 - self.m11 * self.m34 * self.m43 -
+ self.m13 * self.m31 * self.m44 + self.m11 * self.m33 * self.m44),
+ m23: det *
+ (self.m14 * self.m23 * self.m41 - self.m13 * self.m24 * self.m41 -
+ self.m14 * self.m21 * self.m43 + self.m11 * self.m24 * self.m43 +
+ self.m13 * self.m21 * self.m44 - self.m11 * self.m23 * self.m44),
+ m24: det *
+ (self.m13 * self.m24 * self.m31 - self.m14 * self.m23 * self.m31 +
+ self.m14 * self.m21 * self.m33 - self.m11 * self.m24 * self.m33 -
+ self.m13 * self.m21 * self.m34 + self.m11 * self.m23 * self.m34),
+ m31: det *
+ (self.m22 * self.m34 * self.m41 - self.m24 * self.m32 * self.m41 +
+ self.m24 * self.m31 * self.m42 - self.m21 * self.m34 * self.m42 -
+ self.m22 * self.m31 * self.m44 + self.m21 * self.m32 * self.m44),
+ m32: det *
+ (self.m14 * self.m32 * self.m41 - self.m12 * self.m34 * self.m41 -
+ self.m14 * self.m31 * self.m42 + self.m11 * self.m34 * self.m42 +
+ self.m12 * self.m31 * self.m44 - self.m11 * self.m32 * self.m44),
+ m33: det *
+ (self.m12 * self.m24 * self.m41 - self.m14 * self.m22 * self.m41 +
+ self.m14 * self.m21 * self.m42 - self.m11 * self.m24 * self.m42 -
+ self.m12 * self.m21 * self.m44 + self.m11 * self.m22 * self.m44),
+ m34: det *
+ (self.m14 * self.m22 * self.m31 - self.m12 * self.m24 * self.m31 -
+ self.m14 * self.m21 * self.m32 + self.m11 * self.m24 * self.m32 +
+ self.m12 * self.m21 * self.m34 - self.m11 * self.m22 * self.m34),
+ m41: det *
+ (self.m23 * self.m32 * self.m41 - self.m22 * self.m33 * self.m41 -
+ self.m23 * self.m31 * self.m42 + self.m21 * self.m33 * self.m42 +
+ self.m22 * self.m31 * self.m43 - self.m21 * self.m32 * self.m43),
+ m42: det *
+ (self.m12 * self.m33 * self.m41 - self.m13 * self.m32 * self.m41 +
+ self.m13 * self.m31 * self.m42 - self.m11 * self.m33 * self.m42 -
+ self.m12 * self.m31 * self.m43 + self.m11 * self.m32 * self.m43),
+ m43: det *
+ (self.m13 * self.m22 * self.m41 - self.m12 * self.m23 * self.m41 -
+ self.m13 * self.m21 * self.m42 + self.m11 * self.m23 * self.m42 +
+ self.m12 * self.m21 * self.m43 - self.m11 * self.m22 * self.m43),
+ m44: det *
+ (self.m12 * self.m23 * self.m31 - self.m13 * self.m22 * self.m31 +
+ self.m13 * self.m21 * self.m32 - self.m11 * self.m23 * self.m32 -
+ self.m12 * self.m21 * self.m33 + self.m11 * self.m22 * self.m33),
+ };
+
+ Ok(x)
+ }
+
+ /// Multiply `pin * self`.
+ #[inline]
+ pub fn pre_mul_point4(&self, pin: &[f32; 4]) -> [f32; 4] {
+ [
+ pin[0] * self.m11 + pin[1] * self.m21 + pin[2] * self.m31 + pin[3] * self.m41,
+ pin[0] * self.m12 + pin[1] * self.m22 + pin[2] * self.m32 + pin[3] * self.m42,
+ pin[0] * self.m13 + pin[1] * self.m23 + pin[2] * self.m33 + pin[3] * self.m43,
+ pin[0] * self.m14 + pin[1] * self.m24 + pin[2] * self.m34 + pin[3] * self.m44,
+ ]
+ }
+
+ /// Return the multiplication of two 4x4 matrices.
+ #[inline]
+ pub fn multiply(&self, other: &Self) -> Self {
+ Matrix3D {
+ m11: self.m11 * other.m11 + self.m12 * other.m21 +
+ self.m13 * other.m31 + self.m14 * other.m41,
+ m12: self.m11 * other.m12 + self.m12 * other.m22 +
+ self.m13 * other.m32 + self.m14 * other.m42,
+ m13: self.m11 * other.m13 + self.m12 * other.m23 +
+ self.m13 * other.m33 + self.m14 * other.m43,
+ m14: self.m11 * other.m14 + self.m12 * other.m24 +
+ self.m13 * other.m34 + self.m14 * other.m44,
+ m21: self.m21 * other.m11 + self.m22 * other.m21 +
+ self.m23 * other.m31 + self.m24 * other.m41,
+ m22: self.m21 * other.m12 + self.m22 * other.m22 +
+ self.m23 * other.m32 + self.m24 * other.m42,
+ m23: self.m21 * other.m13 + self.m22 * other.m23 +
+ self.m23 * other.m33 + self.m24 * other.m43,
+ m24: self.m21 * other.m14 + self.m22 * other.m24 +
+ self.m23 * other.m34 + self.m24 * other.m44,
+ m31: self.m31 * other.m11 + self.m32 * other.m21 +
+ self.m33 * other.m31 + self.m34 * other.m41,
+ m32: self.m31 * other.m12 + self.m32 * other.m22 +
+ self.m33 * other.m32 + self.m34 * other.m42,
+ m33: self.m31 * other.m13 + self.m32 * other.m23 +
+ self.m33 * other.m33 + self.m34 * other.m43,
+ m34: self.m31 * other.m14 + self.m32 * other.m24 +
+ self.m33 * other.m34 + self.m34 * other.m44,
+ m41: self.m41 * other.m11 + self.m42 * other.m21 +
+ self.m43 * other.m31 + self.m44 * other.m41,
+ m42: self.m41 * other.m12 + self.m42 * other.m22 +
+ self.m43 * other.m32 + self.m44 * other.m42,
+ m43: self.m41 * other.m13 + self.m42 * other.m23 +
+ self.m43 * other.m33 + self.m44 * other.m43,
+ m44: self.m41 * other.m14 + self.m42 * other.m24 +
+ self.m43 * other.m34 + self.m44 * other.m44,
+ }
+ }
+
+ /// Scale the matrix by a factor.
+ #[inline]
+ pub fn scale_by_factor(&mut self, scaling_factor: CSSFloat) {
+ self.m11 *= scaling_factor;
+ self.m12 *= scaling_factor;
+ self.m13 *= scaling_factor;
+ self.m14 *= scaling_factor;
+ self.m21 *= scaling_factor;
+ self.m22 *= scaling_factor;
+ self.m23 *= scaling_factor;
+ self.m24 *= scaling_factor;
+ self.m31 *= scaling_factor;
+ self.m32 *= scaling_factor;
+ self.m33 *= scaling_factor;
+ self.m34 *= scaling_factor;
+ self.m41 *= scaling_factor;
+ self.m42 *= scaling_factor;
+ self.m43 *= scaling_factor;
+ self.m44 *= scaling_factor;
+ }
+
+ /// Return the matrix 3x3 part (top-left corner).
+ /// This is used by retrieving the scale and shear factors
+ /// during decomposing a 3d matrix.
+ #[inline]
+ pub fn get_matrix_3x3_part(&self) -> [[f32; 3]; 3] {
+ [
+ [ self.m11, self.m12, self.m13 ],
+ [ self.m21, self.m22, self.m23 ],
+ [ self.m31, self.m32, self.m33 ],
+ ]
+ }
+
+ /// Set perspective on the matrix.
+ #[inline]
+ pub fn set_perspective(&mut self, perspective: &Perspective) {
+ self.m14 = perspective.0;
+ self.m24 = perspective.1;
+ self.m34 = perspective.2;
+ self.m44 = perspective.3;
+ }
+
+ /// Apply translate on the matrix.
+ #[inline]
+ pub fn apply_translate(&mut self, translate: &Translate3D) {
+ self.m41 += translate.0 * self.m11 + translate.1 * self.m21 + translate.2 * self.m31;
+ self.m42 += translate.0 * self.m12 + translate.1 * self.m22 + translate.2 * self.m32;
+ self.m43 += translate.0 * self.m13 + translate.1 * self.m23 + translate.2 * self.m33;
+ self.m44 += translate.0 * self.m14 + translate.1 * self.m24 + translate.2 * self.m34;
+ }
+
+ /// Apply scale on the matrix.
+ #[inline]
+ pub fn apply_scale(&mut self, scale: &Scale3D) {
+ self.m11 *= scale.0;
+ self.m12 *= scale.0;
+ self.m13 *= scale.0;
+ self.m14 *= scale.0;
+ self.m21 *= scale.1;
+ self.m22 *= scale.1;
+ self.m23 *= scale.1;
+ self.m24 *= scale.1;
+ self.m31 *= scale.2;
+ self.m32 *= scale.2;
+ self.m33 *= scale.2;
+ self.m34 *= scale.2;
+ }
+}
+
+#[cfg_attr(rustfmt, rustfmt_skip)]
+impl Matrix {
+ #[inline]
+ /// Get an identity matrix
+ pub fn identity() -> Self {
+ Self {
+ a: 1., c: 0., /* 0 0*/
+ b: 0., d: 1., /* 0 0*/
+ /* 0 0 1 0 */
+ e: 0., f: 0., /* 0 1 */
+ }
+ }
+}
+
+#[cfg_attr(rustfmt, rustfmt_skip)]
+impl From<Matrix> for Matrix3D {
+ fn from(m: Matrix) -> Self {
+ Self {
+ m11: m.a, m12: m.b, m13: 0.0, m14: 0.0,
+ m21: m.c, m22: m.d, m23: 0.0, m24: 0.0,
+ m31: 0.0, m32: 0.0, m33: 1.0, m34: 0.0,
+ m41: m.e, m42: m.f, m43: 0.0, m44: 1.0
+ }
+ }
+}
+
+#[cfg_attr(rustfmt, rustfmt_skip)]
+impl From<Transform3D<CSSFloat>> for Matrix3D {
+ #[inline]
+ fn from(m: Transform3D<CSSFloat>) -> Self {
+ Matrix3D {
+ m11: m.m11, m12: m.m12, m13: m.m13, m14: m.m14,
+ m21: m.m21, m22: m.m22, m23: m.m23, m24: m.m24,
+ m31: m.m31, m32: m.m32, m33: m.m33, m34: m.m34,
+ m41: m.m41, m42: m.m42, m43: m.m43, m44: m.m44
+ }
+ }
+}
+
+impl TransformOperation {
+ /// Convert to a Translate3D.
+ ///
+ /// Must be called on a Translate function
+ pub fn to_translate_3d(&self) -> Self {
+ match *self {
+ generic::TransformOperation::Translate3D(..) => self.clone(),
+ generic::TransformOperation::TranslateX(ref x) => {
+ generic::TransformOperation::Translate3D(
+ x.clone(),
+ LengthPercentage::zero(),
+ Length::zero(),
+ )
+ },
+ generic::TransformOperation::Translate(ref x, ref y) => {
+ generic::TransformOperation::Translate3D(x.clone(), y.clone(), Length::zero())
+ },
+ generic::TransformOperation::TranslateY(ref y) => {
+ generic::TransformOperation::Translate3D(
+ LengthPercentage::zero(),
+ y.clone(),
+ Length::zero(),
+ )
+ },
+ generic::TransformOperation::TranslateZ(ref z) => {
+ generic::TransformOperation::Translate3D(
+ LengthPercentage::zero(),
+ LengthPercentage::zero(),
+ z.clone(),
+ )
+ },
+ _ => unreachable!(),
+ }
+ }
+
+ /// Convert to a Rotate3D.
+ ///
+ /// Must be called on a Rotate function.
+ pub fn to_rotate_3d(&self) -> Self {
+ match *self {
+ generic::TransformOperation::Rotate3D(..) => self.clone(),
+ generic::TransformOperation::RotateZ(ref angle) |
+ generic::TransformOperation::Rotate(ref angle) => {
+ generic::TransformOperation::Rotate3D(0., 0., 1., angle.clone())
+ },
+ generic::TransformOperation::RotateX(ref angle) => {
+ generic::TransformOperation::Rotate3D(1., 0., 0., angle.clone())
+ },
+ generic::TransformOperation::RotateY(ref angle) => {
+ generic::TransformOperation::Rotate3D(0., 1., 0., angle.clone())
+ },
+ _ => unreachable!(),
+ }
+ }
+
+ /// Convert to a Scale3D.
+ ///
+ /// Must be called on a Scale function
+ pub fn to_scale_3d(&self) -> Self {
+ match *self {
+ generic::TransformOperation::Scale3D(..) => self.clone(),
+ generic::TransformOperation::Scale(x, y) => {
+ generic::TransformOperation::Scale3D(x, y, 1.)
+ },
+ generic::TransformOperation::ScaleX(x) => {
+ generic::TransformOperation::Scale3D(x, 1., 1.)
+ },
+ generic::TransformOperation::ScaleY(y) => {
+ generic::TransformOperation::Scale3D(1., y, 1.)
+ },
+ generic::TransformOperation::ScaleZ(z) => {
+ generic::TransformOperation::Scale3D(1., 1., z)
+ },
+ _ => unreachable!(),
+ }
+ }
+}
+
+/// Build an equivalent 'identity transform function list' based
+/// on an existing transform list.
+/// http://dev.w3.org/csswg/css-transforms/#none-transform-animation
+impl ToAnimatedZero for TransformOperation {
+ fn to_animated_zero(&self) -> Result<Self, ()> {
+ match *self {
+ generic::TransformOperation::Matrix3D(..) => {
+ Ok(generic::TransformOperation::Matrix3D(Matrix3D::identity()))
+ },
+ generic::TransformOperation::Matrix(..) => {
+ Ok(generic::TransformOperation::Matrix(Matrix::identity()))
+ },
+ generic::TransformOperation::Skew(sx, sy) => Ok(generic::TransformOperation::Skew(
+ sx.to_animated_zero()?,
+ sy.to_animated_zero()?,
+ )),
+ generic::TransformOperation::SkewX(s) => {
+ Ok(generic::TransformOperation::SkewX(s.to_animated_zero()?))
+ },
+ generic::TransformOperation::SkewY(s) => {
+ Ok(generic::TransformOperation::SkewY(s.to_animated_zero()?))
+ },
+ generic::TransformOperation::Translate3D(ref tx, ref ty, ref tz) => {
+ Ok(generic::TransformOperation::Translate3D(
+ tx.to_animated_zero()?,
+ ty.to_animated_zero()?,
+ tz.to_animated_zero()?,
+ ))
+ },
+ generic::TransformOperation::Translate(ref tx, ref ty) => {
+ Ok(generic::TransformOperation::Translate(
+ tx.to_animated_zero()?,
+ ty.to_animated_zero()?,
+ ))
+ },
+ generic::TransformOperation::TranslateX(ref t) => Ok(
+ generic::TransformOperation::TranslateX(t.to_animated_zero()?),
+ ),
+ generic::TransformOperation::TranslateY(ref t) => Ok(
+ generic::TransformOperation::TranslateY(t.to_animated_zero()?),
+ ),
+ generic::TransformOperation::TranslateZ(ref t) => Ok(
+ generic::TransformOperation::TranslateZ(t.to_animated_zero()?),
+ ),
+ generic::TransformOperation::Scale3D(..) => {
+ Ok(generic::TransformOperation::Scale3D(1.0, 1.0, 1.0))
+ },
+ generic::TransformOperation::Scale(_, _) => {
+ Ok(generic::TransformOperation::Scale(1.0, 1.0))
+ },
+ generic::TransformOperation::ScaleX(..) => Ok(generic::TransformOperation::ScaleX(1.0)),
+ generic::TransformOperation::ScaleY(..) => Ok(generic::TransformOperation::ScaleY(1.0)),
+ generic::TransformOperation::ScaleZ(..) => Ok(generic::TransformOperation::ScaleZ(1.0)),
+ generic::TransformOperation::Rotate3D(x, y, z, a) => {
+ let (x, y, z, _) = generic::get_normalized_vector_and_angle(x, y, z, a);
+ Ok(generic::TransformOperation::Rotate3D(
+ x,
+ y,
+ z,
+ Angle::zero(),
+ ))
+ },
+ generic::TransformOperation::RotateX(_) => {
+ Ok(generic::TransformOperation::RotateX(Angle::zero()))
+ },
+ generic::TransformOperation::RotateY(_) => {
+ Ok(generic::TransformOperation::RotateY(Angle::zero()))
+ },
+ generic::TransformOperation::RotateZ(_) => {
+ Ok(generic::TransformOperation::RotateZ(Angle::zero()))
+ },
+ generic::TransformOperation::Rotate(_) => {
+ Ok(generic::TransformOperation::Rotate(Angle::zero()))
+ },
+ generic::TransformOperation::Perspective(_) => Ok(
+ generic::TransformOperation::Perspective(generic::PerspectiveFunction::None),
+ ),
+ generic::TransformOperation::AccumulateMatrix { .. } |
+ generic::TransformOperation::InterpolateMatrix { .. } => {
+ // AccumulateMatrix/InterpolateMatrix: We do interpolation on
+ // AccumulateMatrix/InterpolateMatrix by reading it as a ComputedMatrix
+ // (with layout information), and then do matrix interpolation.
+ //
+ // Therefore, we use an identity matrix to represent the identity transform list.
+ // http://dev.w3.org/csswg/css-transforms/#identity-transform-function
+ Ok(generic::TransformOperation::Matrix3D(Matrix3D::identity()))
+ },
+ }
+ }
+}
+
+impl ToAnimatedZero for Transform {
+ #[inline]
+ fn to_animated_zero(&self) -> Result<Self, ()> {
+ Ok(generic::Transform(
+ self.0
+ .iter()
+ .map(|op| op.to_animated_zero())
+ .collect::<Result<crate::OwnedSlice<_>, _>>()?,
+ ))
+ }
+}
+
+/// A computed CSS `rotate`
+pub type Rotate = generic::GenericRotate<Number, Angle>;
+
+/// A computed CSS `translate`
+pub type Translate = generic::GenericTranslate<LengthPercentage, Length>;
+
+/// A computed CSS `scale`
+pub type Scale = generic::GenericScale<Number>;
diff --git a/servo/components/style/values/computed/ui.rs b/servo/components/style/values/computed/ui.rs
new file mode 100644
index 0000000000..f285c0626b
--- /dev/null
+++ b/servo/components/style/values/computed/ui.rs
@@ -0,0 +1,21 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Computed values for UI properties
+
+use crate::values::computed::color::Color;
+use crate::values::computed::image::Image;
+use crate::values::computed::Number;
+use crate::values::generics::ui as generics;
+
+pub use crate::values::specified::ui::{BoolInteger, CursorKind, MozTheme, UserSelect};
+
+/// A computed value for the `cursor` property.
+pub type Cursor = generics::GenericCursor<CursorImage>;
+
+/// A computed value for item of `image cursors`.
+pub type CursorImage = generics::GenericCursorImage<Image, Number>;
+
+/// A computed value for `scrollbar-color` property.
+pub type ScrollbarColor = generics::GenericScrollbarColor<Color>;
diff --git a/servo/components/style/values/computed/url.rs b/servo/components/style/values/computed/url.rs
new file mode 100644
index 0000000000..9f0d8f5bb3
--- /dev/null
+++ b/servo/components/style/values/computed/url.rs
@@ -0,0 +1,15 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Common handling for the computed value CSS url() values.
+
+use crate::values::generics::url::UrlOrNone as GenericUrlOrNone;
+
+#[cfg(feature = "gecko")]
+pub use crate::gecko::url::{ComputedImageUrl, ComputedUrl};
+#[cfg(feature = "servo")]
+pub use crate::servo::url::{ComputedImageUrl, ComputedUrl};
+
+/// Computed <url> | <none>
+pub type UrlOrNone = GenericUrlOrNone<ComputedUrl>;