servo: Merge #18301 - Use generics for the vertical-align property (from servo:derive-all-the-things); r=emilio
authorAnthony Ramine <n.oxyde@gmail.com>
Thu, 31 Aug 2017 09:41:50 -0500
changeset 378072 803a7a310b109b75db75bdd2e1787bee7cd9ab8c
parent 378071 3ae0fa531315c6707205058f6a54a7cb7a027286
child 378073 5ffb2108ba511b94e52bd0686f29b71fe8bbe79d
push id94412
push userarchaeopteryx@coole-files.de
push dateFri, 01 Sep 2017 08:46:09 +0000
treeherdermozilla-inbound@d56571d7f1be [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
servo: Merge #18301 - Use generics for the vertical-align property (from servo:derive-all-the-things); r=emilio Source-Repo: https://github.com/servo/servo Source-Revision: 174c37fddd3ba66797efdb8067c24003f32c04a7
servo/components/layout/fragment.rs
servo/components/layout/inline.rs
servo/components/layout/table_cell.rs
servo/components/style/gecko/conversions.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/helpers/animated_properties.mako.rs
servo/components/style/properties/longhand/box.mako.rs
servo/components/style/values/computed/box.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/generics/box.rs
servo/components/style/values/generics/mod.rs
servo/components/style/values/specified/box.rs
servo/components/style/values/specified/mod.rs
--- a/servo/components/layout/fragment.rs
+++ b/servo/components/layout/fragment.rs
@@ -37,26 +37,27 @@ use serde::ser::{Serialize, SerializeStr
 use servo_url::ServoUrl;
 use std::{f32, fmt};
 use std::borrow::ToOwned;
 use std::cmp::{Ordering, max, min};
 use std::collections::LinkedList;
 use std::sync::{Arc, Mutex};
 use style::computed_values::{border_collapse, box_sizing, clear, color, display, mix_blend_mode};
 use style::computed_values::{overflow_wrap, overflow_x, position, text_decoration_line};
-use style::computed_values::{transform_style, vertical_align, white_space, word_break};
+use style::computed_values::{transform_style, white_space, word_break};
 use style::computed_values::content::ContentItem;
 use style::logical_geometry::{Direction, LogicalMargin, LogicalRect, LogicalSize, WritingMode};
 use style::properties::ComputedValues;
 use style::properties::longhands::transform::computed_value::T as TransformList;
 use style::selector_parser::RestyleDamage;
 use style::servo::restyle_damage::RECONSTRUCT_FLOW;
 use style::str::char_is_whitespace;
 use style::values::{self, Either, Auto};
 use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};
+use style::values::generics::box_::VerticalAlign;
 use text;
 use text::TextRunScanner;
 use webrender_api;
 use wrapper::ThreadSafeLayoutNodeHelpers;
 
 // From gfxFontConstants.h in Firefox.
 static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20;
 static FONT_SUPERSCRIPT_OFFSET_RATIO: f32 = 0.34;
@@ -2217,61 +2218,60 @@ impl Fragment {
         let mut offset = Au(0);
         for style in self.inline_styles() {
             // If any of the inline styles say `top` or `bottom`, adjust the vertical align
             // appropriately.
             //
             // FIXME(#5624, pcwalton): This passes our current reftests but isn't the right thing
             // to do.
             match style.get_box().vertical_align {
-                vertical_align::T::baseline => {}
-                vertical_align::T::middle => {
+                VerticalAlign::Baseline => {}
+                VerticalAlign::Middle => {
                     let font_metrics = with_thread_local_font_context(layout_context, |font_context| {
                         text::font_metrics_for_style(font_context, self.style.clone_font())
                     });
                     offset += (content_inline_metrics.ascent -
                                content_inline_metrics.space_below_baseline -
                                font_metrics.x_height).scale_by(0.5)
                 }
-                vertical_align::T::sub => {
+                VerticalAlign::Sub => {
                     offset += minimum_line_metrics.space_needed()
                                                   .scale_by(FONT_SUBSCRIPT_OFFSET_RATIO)
                 }
-                vertical_align::T::super_ => {
+                VerticalAlign::Super => {
                     offset -= minimum_line_metrics.space_needed()
                                                   .scale_by(FONT_SUPERSCRIPT_OFFSET_RATIO)
                 }
-                vertical_align::T::text_top => {
+                VerticalAlign::TextTop => {
                     offset = self.content_inline_metrics(layout_context).ascent -
                         minimum_line_metrics.space_above_baseline
                 }
-                vertical_align::T::text_bottom => {
+                VerticalAlign::TextBottom => {
                     offset = minimum_line_metrics.space_below_baseline -
                         self.content_inline_metrics(layout_context).space_below_baseline
                 }
-                vertical_align::T::top => {
+                VerticalAlign::Top => {
                     if let Some(actual_line_metrics) = actual_line_metrics {
                         offset = content_inline_metrics.ascent -
                             actual_line_metrics.space_above_baseline
                     }
                 }
-                vertical_align::T::bottom => {
+                VerticalAlign::Bottom => {
                     if let Some(actual_line_metrics) = actual_line_metrics {
                         offset = actual_line_metrics.space_below_baseline -
                             content_inline_metrics.space_below_baseline
                     }
                 }
-                vertical_align::T::LengthOrPercentage(LengthOrPercentage::Length(length)) => {
+                VerticalAlign::Length(LengthOrPercentage::Length(length)) => {
                     offset -= length
                 }
-                vertical_align::T::LengthOrPercentage(LengthOrPercentage::Percentage(
-                        percentage)) => {
+                VerticalAlign::Length(LengthOrPercentage::Percentage(percentage)) => {
                     offset -= minimum_line_metrics.space_needed().scale_by(percentage.0)
                 }
-                vertical_align::T::LengthOrPercentage(LengthOrPercentage::Calc(formula)) => {
+                VerticalAlign::Length(LengthOrPercentage::Calc(formula)) => {
                     offset -= formula.to_used_value(Some(minimum_line_metrics.space_needed())).unwrap()
                 }
             }
         }
         offset
     }
 
     /// Calculates block-size above baseline, depth below baseline, and ascent for this fragment
@@ -2819,23 +2819,23 @@ impl Fragment {
             }
         }
     }
 
     /// Returns true if any of the inline styles associated with this fragment have
     /// `vertical-align` set to `top` or `bottom`.
     pub fn is_vertically_aligned_to_top_or_bottom(&self) -> bool {
         match self.style.get_box().vertical_align {
-            vertical_align::T::top | vertical_align::T::bottom => return true,
+            VerticalAlign::Top | VerticalAlign::Bottom => return true,
             _ => {}
         }
         if let Some(ref inline_context) = self.inline_context {
             for node in &inline_context.nodes {
                 match node.style.get_box().vertical_align {
-                    vertical_align::T::top | vertical_align::T::bottom => return true,
+                    VerticalAlign::Top | VerticalAlign::Bottom => return true,
                     _ => {}
                 }
             }
         }
         false
     }
 
     pub fn is_text_or_replaced(&self) -> bool {
--- a/servo/components/layout/inline.rs
+++ b/servo/components/layout/inline.rs
@@ -29,16 +29,17 @@ use std::{fmt, i32, isize, mem};
 use std::cmp::max;
 use std::collections::VecDeque;
 use std::sync::Arc;
 use style::computed_values::{display, overflow_x, position, text_align, text_justify};
 use style::computed_values::{vertical_align, white_space};
 use style::logical_geometry::{LogicalRect, LogicalSize, WritingMode};
 use style::properties::{longhands, ComputedValues};
 use style::servo::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, RESOLVE_GENERATED_CONTENT};
+use style::values::generics::box_::VerticalAlign;
 use text;
 use traversal::PreorderFlowTraversal;
 use unicode_bidi as bidi;
 
 /// `Line`s are represented as offsets into the child list, rather than
 /// as an object that "owns" fragments. Choosing a different set of line
 /// breaks requires a new list of offsets, and possibly some splitting and
 /// merging of TextFragments.
@@ -1131,22 +1132,22 @@ impl InlineFlow {
         let font_metrics = text::font_metrics_for_style(font_context, font_style);
         let line_height = text::line_height_from_style(style, &font_metrics);
         let inline_metrics = InlineMetrics::from_font_metrics(&font_metrics, line_height);
 
         let mut line_metrics = LineMetrics::new(Au(0), MIN_AU);
         let mut largest_block_size_for_top_fragments = Au(0);
         let mut largest_block_size_for_bottom_fragments = Au(0);
 
-        // We use `vertical_align::T::baseline` here because `vertical-align` must not apply to
-        // the inside of inline blocks.
+        // We use `VerticalAlign::Baseline` here because `vertical-align` must
+        // not apply to the inside of inline blocks.
         update_line_metrics_for_fragment(&mut line_metrics,
                                          &inline_metrics,
                                          style.get_box().display,
-                                         vertical_align::T::baseline,
+                                         VerticalAlign::Baseline,
                                          &mut largest_block_size_for_top_fragments,
                                          &mut largest_block_size_for_bottom_fragments);
 
         // According to CSS 2.1 ยง 10.8, `line-height` of any inline element specifies the minimal
         // height of line boxes within the element.
         for inline_context in fragments.iter()
                                        .filter_map(|fragment| fragment.inline_context.as_ref()) {
             for node in &inline_context.nodes {
@@ -1177,29 +1178,29 @@ impl InlineFlow {
 
         fn update_line_metrics_for_fragment(line_metrics: &mut LineMetrics,
                                             inline_metrics: &InlineMetrics,
                                             display_value: display::T,
                                             vertical_align_value: vertical_align::T,
                                             largest_block_size_for_top_fragments: &mut Au,
                                             largest_block_size_for_bottom_fragments: &mut Au) {
             match (display_value, vertical_align_value) {
-                (display::T::inline, vertical_align::T::top) |
-                (display::T::block, vertical_align::T::top) |
-                (display::T::inline_flex, vertical_align::T::top) |
-                (display::T::inline_block, vertical_align::T::top) if
+                (display::T::inline, VerticalAlign::Top) |
+                (display::T::block, VerticalAlign::Top) |
+                (display::T::inline_flex, VerticalAlign::Top) |
+                (display::T::inline_block, VerticalAlign::Top) if
                         inline_metrics.space_above_baseline >= Au(0) => {
                     *largest_block_size_for_top_fragments = max(
                         *largest_block_size_for_top_fragments,
                         inline_metrics.space_above_baseline + inline_metrics.space_below_baseline)
                 }
-                (display::T::inline, vertical_align::T::bottom) |
-                (display::T::block, vertical_align::T::bottom) |
-                (display::T::inline_flex, vertical_align::T::bottom) |
-                (display::T::inline_block, vertical_align::T::bottom) if
+                (display::T::inline, VerticalAlign::Bottom) |
+                (display::T::block, VerticalAlign::Bottom) |
+                (display::T::inline_flex, VerticalAlign::Bottom) |
+                (display::T::inline_block, VerticalAlign::Bottom) if
                         inline_metrics.space_below_baseline >= Au(0) => {
                     *largest_block_size_for_bottom_fragments = max(
                         *largest_block_size_for_bottom_fragments,
                         inline_metrics.space_above_baseline + inline_metrics.space_below_baseline)
                 }
                 _ => *line_metrics = line_metrics.new_metrics_for_fragment(inline_metrics),
             }
         }
--- a/servo/components/layout/table_cell.rs
+++ b/servo/components/layout/table_cell.rs
@@ -13,20 +13,21 @@ use display_list_builder::{BlockFlowDisp
 use euclid::{Point2D, Rect, SideOffsets2D, Size2D};
 use flow::{self, Flow, FlowClass, IS_ABSOLUTELY_POSITIONED, OpaqueFlow};
 use fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
 use gfx_traits::print_tree::PrintTree;
 use layout_debug;
 use model::MaybeAuto;
 use script_layout_interface::wrapper_traits::ThreadSafeLayoutNode;
 use std::fmt;
-use style::computed_values::{border_collapse, border_top_style, vertical_align};
+use style::computed_values::{border_collapse, border_top_style};
 use style::logical_geometry::{LogicalMargin, LogicalRect, LogicalSize, WritingMode};
 use style::properties::ComputedValues;
 use style::values::computed::Color;
+use style::values::generics::box_::VerticalAlign;
 use table::InternalTable;
 use table_row::{CollapsedBorder, CollapsedBorderProvenance};
 
 /// A table formatting context.
 #[derive(Serialize)]
 pub struct TableCellFlow {
     /// Data common to all block flows.
     pub block_flow: BlockFlow,
@@ -119,21 +120,21 @@ impl TableCellFlow {
             None => return,
         };
 
         let kids_size = last_end - first_start;
         let self_size = flow::base(self).position.size.block -
             self.block_flow.fragment.border_padding.block_start_end();
         let kids_self_gap = self_size - kids_size;
 
-        // This offset should also account for vertical_align::T::baseline.
+        // This offset should also account for VerticalAlign::Baseline.
         // Need max cell ascent from the first row of this cell.
         let offset = match self.block_flow.fragment.style().get_box().vertical_align {
-            vertical_align::T::middle => kids_self_gap / 2,
-            vertical_align::T::bottom => kids_self_gap,
+            VerticalAlign::Middle => kids_self_gap / 2,
+            VerticalAlign::Bottom => kids_self_gap,
             _ => Au(0),
         };
         if offset == Au(0) {
             return
         }
 
         for kid in flow::mut_base(self).children.iter_mut() {
             let kid_base = flow::mut_base(kid);
--- a/servo/components/style/gecko/conversions.rs
+++ b/servo/components/style/gecko/conversions.rs
@@ -7,23 +7,24 @@
 //! forces us to keep the traits and implementations here
 
 #![allow(unsafe_code)]
 
 use app_units::Au;
 use gecko::values::{convert_rgba_to_nscolor, GeckoStyleCoordConvertible};
 use gecko_bindings::bindings::{Gecko_CreateGradient, Gecko_SetGradientImageValue, Gecko_SetLayerImageImageValue};
 use gecko_bindings::bindings::{Gecko_InitializeImageCropRect, Gecko_SetImageElement};
-use gecko_bindings::structs::{nsCSSUnit, nsStyleCoord_CalcValue, nsStyleImage};
-use gecko_bindings::structs::{nsresult, SheetType};
+use gecko_bindings::structs::{self, nsCSSUnit, nsStyleCoord_CalcValue};
+use gecko_bindings::structs::{nsStyleImage, nsresult, SheetType};
 use gecko_bindings::sugar::ns_style_coord::{CoordDataValue, CoordData, CoordDataMut};
 use std::f32::consts::PI;
 use stylesheets::{Origin, RulesMutateError};
 use values::computed::{Angle, CalcLengthOrPercentage, Gradient, Image};
 use values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto, Percentage};
+use values::generics::box_::VerticalAlign;
 use values::generics::grid::TrackSize;
 use values::generics::image::{CompatMode, Image as GenericImage, GradientItem};
 use values::generics::rect::Rect;
 use values::specified::url::SpecifiedUrl;
 
 impl From<CalcLengthOrPercentage> for nsStyleCoord_CalcValue {
     fn from(other: CalcLengthOrPercentage) -> nsStyleCoord_CalcValue {
         let has_percentage = other.percentage.is_some();
@@ -915,16 +916,36 @@ impl<T> Rect<T> where T: GeckoStyleCoord
                 T::from_gecko_style_coord(&sides.data_at(1)).expect("coord[1] cound not convert"),
                 T::from_gecko_style_coord(&sides.data_at(2)).expect("coord[2] cound not convert"),
                 T::from_gecko_style_coord(&sides.data_at(3)).expect("coord[3] cound not convert")
             )
         )
     }
 }
 
+impl<L> VerticalAlign<L> {
+    /// Converts an enumerated value coming from Gecko to a `VerticalAlign<L>`.
+    pub fn from_gecko_keyword(value: u32) -> Self {
+        match value {
+            structs::NS_STYLE_VERTICAL_ALIGN_BASELINE => VerticalAlign::Baseline,
+            structs::NS_STYLE_VERTICAL_ALIGN_SUB => VerticalAlign::Sub,
+            structs::NS_STYLE_VERTICAL_ALIGN_SUPER => VerticalAlign::Super,
+            structs::NS_STYLE_VERTICAL_ALIGN_TOP => VerticalAlign::Top,
+            structs::NS_STYLE_VERTICAL_ALIGN_TEXT_TOP => VerticalAlign::TextTop,
+            structs::NS_STYLE_VERTICAL_ALIGN_MIDDLE => VerticalAlign::Middle,
+            structs::NS_STYLE_VERTICAL_ALIGN_BOTTOM => VerticalAlign::Bottom,
+            structs::NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM => VerticalAlign::TextBottom,
+            structs::NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE => {
+                VerticalAlign::MozMiddleWithBaseline
+            },
+            _ => panic!("unexpected enumerated value for vertical-align"),
+        }
+    }
+}
+
 /// Convert to String from given chars pointer.
 pub unsafe fn string_from_chars_pointer(p: *const u16) -> String {
     use std::slice;
     let mut length = 0;
     let mut iter = p;
     while *iter != 0 {
         length += 1;
         iter = iter.offset(1);
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -2899,43 +2899,51 @@ fn static_assert() {
             % for value in overflow_x.keyword.values_for('gecko'):
             structs::${overflow_x.keyword.gecko_constant(value)} => BaseType::${to_rust_ident(value)},
             % endfor
             x => panic!("Found unexpected value in style struct for overflow_y property: {}", x),
         }
     }
 
     pub fn set_vertical_align(&mut self, v: longhands::vertical_align::computed_value::T) {
-        <% keyword = data.longhands_by_name["vertical-align"].keyword %>
-        use properties::longhands::vertical_align::computed_value::T;
-        // FIXME: Align binary representations and ditch |match| for cast + static_asserts
-        match v {
-            % for value in keyword.values_for('gecko'):
-                T::${to_rust_ident(value)} =>
-                    self.gecko.mVerticalAlign.set_value(
-                            CoordDataValue::Enumerated(structs::${keyword.gecko_constant(value)})),
-            % endfor
-            T::LengthOrPercentage(v) => self.gecko.mVerticalAlign.set(v),
-        }
+        use values::generics::box_::VerticalAlign;
+        let value = match v {
+            VerticalAlign::Baseline => structs::NS_STYLE_VERTICAL_ALIGN_BASELINE,
+            VerticalAlign::Sub => structs::NS_STYLE_VERTICAL_ALIGN_SUB,
+            VerticalAlign::Super => structs::NS_STYLE_VERTICAL_ALIGN_SUPER,
+            VerticalAlign::Top => structs::NS_STYLE_VERTICAL_ALIGN_TOP,
+            VerticalAlign::TextTop => structs::NS_STYLE_VERTICAL_ALIGN_TEXT_TOP,
+            VerticalAlign::Middle => structs::NS_STYLE_VERTICAL_ALIGN_MIDDLE,
+            VerticalAlign::Bottom => structs::NS_STYLE_VERTICAL_ALIGN_BOTTOM,
+            VerticalAlign::TextBottom => structs::NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM,
+            VerticalAlign::MozMiddleWithBaseline => {
+                structs::NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE
+            },
+            VerticalAlign::Length(length) => {
+                self.gecko.mVerticalAlign.set(length);
+                return;
+            },
+        };
+        self.gecko.mVerticalAlign.set_value(CoordDataValue::Enumerated(value));
     }
 
     pub fn clone_vertical_align(&self) -> longhands::vertical_align::computed_value::T {
-        use properties::longhands::vertical_align::computed_value::T;
         use values::computed::LengthOrPercentage;
-
-        match self.gecko.mVerticalAlign.as_value() {
-            % for value in keyword.values_for('gecko'):
-                CoordDataValue::Enumerated(structs::${keyword.gecko_constant(value)}) => T::${to_rust_ident(value)},
-            % endfor
-                CoordDataValue::Enumerated(_) => panic!("Unexpected enum variant for vertical-align"),
-                _ => {
-                    let v = LengthOrPercentage::from_gecko_style_coord(&self.gecko.mVerticalAlign)
-                        .expect("Expected length or percentage for vertical-align");
-                    T::LengthOrPercentage(v)
-                }
+        use values::generics::box_::VerticalAlign;
+
+        let gecko = &self.gecko.mVerticalAlign;
+        match gecko.as_value() {
+            CoordDataValue::Enumerated(value) => VerticalAlign::from_gecko_keyword(value),
+            _ => {
+                VerticalAlign::Length(
+                    LengthOrPercentage::from_gecko_style_coord(gecko).expect(
+                        "expected <length-percentage> for vertical-align",
+                    ),
+                )
+            },
         }
     }
 
     <%call expr="impl_coord_copy('vertical_align', 'mVerticalAlign')"></%call>
 
     % for kind in ["before", "after"]:
     // Temp fix for Bugzilla bug 24000.
     // Map 'auto' and 'avoid' to false, and 'always', 'left', and 'right' to true.
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -19,17 +19,16 @@ use properties::longhands;
 use properties::longhands::background_size::computed_value::T as BackgroundSizeList;
 use properties::longhands::border_spacing::computed_value::T as BorderSpacing;
 use properties::longhands::font_weight::computed_value::T as FontWeight;
 use properties::longhands::font_stretch::computed_value::T as FontStretch;
 use properties::longhands::line_height::computed_value::T as LineHeight;
 use properties::longhands::transform::computed_value::ComputedMatrix;
 use properties::longhands::transform::computed_value::ComputedOperation as TransformOperation;
 use properties::longhands::transform::computed_value::T as TransformList;
-use properties::longhands::vertical_align::computed_value::T as VerticalAlign;
 use properties::longhands::visibility::computed_value::T as Visibility;
 #[cfg(feature = "gecko")] use properties::{PropertyId, PropertyDeclarationId, LonghandId};
 #[cfg(feature = "gecko")] use properties::{ShorthandId};
 use selectors::parser::SelectorParseError;
 use smallvec::SmallVec;
 use std::borrow::Cow;
 use std::cmp;
 #[cfg(feature = "gecko")] use fnv::FnvHashMap;
@@ -782,21 +781,16 @@ impl ComputeSquaredDistance for Visibili
 
 impl ToAnimatedZero for Visibility {
     #[inline]
     fn to_animated_zero(&self) -> Result<Self, ()> {
         Err(())
     }
 }
 
-impl ToAnimatedZero for VerticalAlign {
-    #[inline]
-    fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) }
-}
-
 /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
 impl Animate for CalcLengthOrPercentage {
     #[inline]
     fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
         let animate_percentage_half = |this: Option<Percentage>, other: Option<Percentage>| {
             if this.is_none() && other.is_none() {
                 return Ok(None);
             }
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.mako.rs
@@ -302,135 +302,25 @@
     #[inline]
     pub fn derive_from_display(context: &mut Context) {
         let d = context.style().get_box().clone_display();
         context.builder.set__servo_display_for_hypothetical_box(d);
     }
 
 </%helpers:longhand>
 
-<%helpers:longhand name="vertical-align" animation_value_type="ComputedValue"
-                   flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
-                   spec="https://www.w3.org/TR/CSS2/visudet.html#propdef-vertical-align">
-    use std::fmt;
-    use style_traits::ToCss;
-    use values::specified::AllowQuirks;
 
-    <% vertical_align = data.longhands_by_name["vertical-align"] %>
-    <% vertical_align.keyword = Keyword("vertical-align",
-                                        "baseline sub super top text-top middle bottom text-bottom",
-                                        extra_gecko_values="-moz-middle-with-baseline") %>
-    <% vertical_align_keywords = vertical_align.keyword.values_for(product) %>
-
-    ${helpers.gecko_keyword_conversion(vertical_align.keyword)}
-
-    /// The `vertical-align` value.
-    #[allow(non_camel_case_types)]
-    #[derive(Clone, Debug, PartialEq)]
-    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-    pub enum SpecifiedValue {
-        % for keyword in vertical_align_keywords:
-            ${to_rust_ident(keyword)},
-        % endfor
-        LengthOrPercentage(specified::LengthOrPercentage),
-    }
-
-    impl ToCss for SpecifiedValue {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            match *self {
-                % for keyword in vertical_align_keywords:
-                    SpecifiedValue::${to_rust_ident(keyword)} => dest.write_str("${keyword}"),
-                % endfor
-                SpecifiedValue::LengthOrPercentage(ref value) => value.to_css(dest),
-            }
-        }
-    }
-    /// baseline | sub | super | top | text-top | middle | bottom | text-bottom
-    /// | <percentage> | <length>
-    pub fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
-                         -> Result<SpecifiedValue, ParseError<'i>> {
-        if let Ok(lop) = input.try(|i| specified::LengthOrPercentage::parse_quirky(context, i, AllowQuirks::Yes)) {
-            return Ok(SpecifiedValue::LengthOrPercentage(lop));
-        }
-
-        try_match_ident_ignore_ascii_case! { input.expect_ident()?,
-            % for keyword in vertical_align_keywords:
-                "${keyword}" => Ok(SpecifiedValue::${to_rust_ident(keyword)}),
-            % endfor
-        }
-    }
-
-    /// The computed value for `vertical-align`.
-    pub mod computed_value {
-        use std::fmt;
-        use style_traits::ToCss;
-        use values::computed;
-
-        /// The keywords are the same, and the `LengthOrPercentage` is computed
-        /// here.
-        #[allow(non_camel_case_types)]
-        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-        #[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, PartialEq)]
-        pub enum T {
-            % for keyword in vertical_align_keywords:
-            #[animation(error)] // only animatable as a length
-            ${to_rust_ident(keyword)},
-            % endfor
-            LengthOrPercentage(computed::LengthOrPercentage),
-        }
-        impl ToCss for T {
-            fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-                match *self {
-                    % for keyword in vertical_align_keywords:
-                        T::${to_rust_ident(keyword)} => dest.write_str("${keyword}"),
-                    % endfor
-                    T::LengthOrPercentage(ref value) => value.to_css(dest),
-                }
-            }
-        }
-    }
-
-    /// The initial computed value for `vertical-align`.
-    #[inline]
-    pub fn get_initial_value() -> computed_value::T {
-        computed_value::T::baseline
-    }
-
-    impl ToComputedValue for SpecifiedValue {
-        type ComputedValue = computed_value::T;
-
-        #[inline]
-        fn to_computed_value(&self, context: &Context) -> computed_value::T {
-            match *self {
-                % for keyword in vertical_align_keywords:
-                    SpecifiedValue::${to_rust_ident(keyword)} => {
-                        computed_value::T::${to_rust_ident(keyword)}
-                    }
-                % endfor
-                SpecifiedValue::LengthOrPercentage(ref value) =>
-                    computed_value::T::LengthOrPercentage(value.to_computed_value(context)),
-            }
-        }
-        #[inline]
-        fn from_computed_value(computed: &computed_value::T) -> Self {
-            match *computed {
-                % for keyword in vertical_align_keywords:
-                    computed_value::T::${to_rust_ident(keyword)} => {
-                        SpecifiedValue::${to_rust_ident(keyword)}
-                    }
-                % endfor
-                computed_value::T::LengthOrPercentage(value) =>
-                    SpecifiedValue::LengthOrPercentage(
-                      ToComputedValue::from_computed_value(&value)
-                    ),
-            }
-        }
-    }
-</%helpers:longhand>
-
+${helpers.predefined_type(
+    "vertical-align",
+    "VerticalAlign",
+    "computed::VerticalAlign::baseline()",
+    animation_value_type="ComputedValue",
+    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
+    spec="https://www.w3.org/TR/CSS2/visudet.html#propdef-vertical-align",
+)}
 
 // CSS 2.1, Section 11 - Visual effects
 
 ${helpers.single_keyword("-servo-overflow-clip-box", "padding-box content-box",
     products="servo", animation_value_type="none", internal=True,
     spec="Internal, not web-exposed, \
           may be standardized in the future (https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-clip-box)")}
 
new file mode 100644
--- /dev/null
+++ b/servo/components/style/values/computed/box.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 http://mozilla.org/MPL/2.0/. */
+
+//! Computed types for box properties.
+
+use values::computed::length::LengthOrPercentage;
+use values::generics::box_::VerticalAlign as GenericVerticalAlign;
+
+/// A computed value for the `vertical-align` property.
+pub type VerticalAlign = GenericVerticalAlign<LengthOrPercentage>;
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -29,16 +29,17 @@ use super::specified;
 pub use app_units::Au;
 pub use properties::animated_properties::TransitionProperty;
 #[cfg(feature = "gecko")]
 pub use self::align::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
 pub use self::angle::Angle;
 pub use self::background::BackgroundSize;
 pub use self::border::{BorderImageSlice, BorderImageWidth, BorderImageSideWidth};
 pub use self::border::{BorderRadius, BorderCornerRadius};
+pub use self::box_::VerticalAlign;
 pub use self::color::{Color, RGBAColor};
 pub use self::effects::{BoxShadow, Filter, SimpleShadow};
 pub use self::flex::FlexBasis;
 pub use self::image::{Gradient, GradientItem, Image, ImageLayer, LineDirection, MozImageRect};
 #[cfg(feature = "gecko")]
 pub use self::gecko::ScrollSnapPoint;
 pub use self::rect::LengthOrNumberRect;
 pub use super::{Auto, Either, None_};
@@ -55,16 +56,18 @@ pub use self::time::Time;
 pub use self::transform::{TimingFunction, TransformOrigin};
 
 #[cfg(feature = "gecko")]
 pub mod align;
 pub mod angle;
 pub mod background;
 pub mod basic_shape;
 pub mod border;
+#[path = "box.rs"]
+pub mod box_;
 pub mod color;
 pub mod effects;
 pub mod flex;
 pub mod image;
 #[cfg(feature = "gecko")]
 pub mod gecko;
 pub mod length;
 pub mod percentage;
new file mode 100644
--- /dev/null
+++ b/servo/components/style/values/generics/box.rs
@@ -0,0 +1,49 @@
+/* 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 http://mozilla.org/MPL/2.0/. */
+
+//! Generic types for box properties.
+
+use values::animated::ToAnimatedZero;
+
+/// A generic value for the `vertical-align` property.
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, PartialEq)]
+#[derive(ToComputedValue, ToCss)]
+pub enum VerticalAlign<LengthOrPercentage> {
+    /// `baseline`
+    Baseline,
+    /// `sub`
+    Sub,
+    /// `super`
+    Super,
+    /// `top`
+    Top,
+    /// `text-top`
+    TextTop,
+    /// `middle`
+    Middle,
+    /// `bottom`
+    Bottom,
+    /// `text-bottom`
+    TextBottom,
+    /// `-moz-middle-with-baseline`
+    #[cfg(feature = "gecko")]
+    MozMiddleWithBaseline,
+    /// `<length-percentage>`
+    Length(LengthOrPercentage),
+}
+
+impl<L> VerticalAlign<L> {
+    /// Returns `baseline`.
+    #[inline]
+    pub fn baseline() -> Self {
+        VerticalAlign::Baseline
+    }
+}
+
+impl<L> ToAnimatedZero for VerticalAlign<L> {
+    fn to_animated_zero(&self) -> Result<Self, ()> {
+        Err(())
+    }
+}
--- a/servo/components/style/values/generics/mod.rs
+++ b/servo/components/style/values/generics/mod.rs
@@ -10,16 +10,18 @@ use cssparser::Parser;
 use parser::{Parse, ParserContext};
 use std::fmt;
 use style_traits::{Comma, OneOrMoreSeparated, ParseError, StyleParseError, ToCss};
 use super::CustomIdent;
 
 pub mod background;
 pub mod basic_shape;
 pub mod border;
+#[path = "box.rs"]
+pub mod box_;
 pub mod effects;
 pub mod flex;
 #[cfg(feature = "gecko")]
 pub mod gecko;
 pub mod grid;
 pub mod image;
 pub mod position;
 pub mod rect;
new file mode 100644
--- /dev/null
+++ b/servo/components/style/values/specified/box.rs
@@ -0,0 +1,41 @@
+/* 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 http://mozilla.org/MPL/2.0/. */
+
+//! Specified types for box properties.
+
+use cssparser::Parser;
+use parser::{Parse, ParserContext};
+use style_traits::ParseError;
+use values::generics::box_::VerticalAlign as GenericVerticalAlign;
+use values::specified::AllowQuirks;
+use values::specified::length::LengthOrPercentage;
+
+/// A specified value for the `vertical-align` property.
+pub type VerticalAlign = GenericVerticalAlign<LengthOrPercentage>;
+
+impl Parse for VerticalAlign {
+    fn parse<'i, 't>(
+        context: &ParserContext,
+        input: &mut Parser<'i, 't>,
+    ) -> Result<Self, ParseError<'i>> {
+        if let Ok(lop) = input.try(|i| LengthOrPercentage::parse_quirky(context, i, AllowQuirks::Yes)) {
+            return Ok(GenericVerticalAlign::Length(lop));
+        }
+
+        try_match_ident_ignore_ascii_case! { input.expect_ident()?,
+            "baseline" => Ok(GenericVerticalAlign::Baseline),
+            "sub" => Ok(GenericVerticalAlign::Sub),
+            "super" => Ok(GenericVerticalAlign::Super),
+            "top" => Ok(GenericVerticalAlign::Top),
+            "text-top" => Ok(GenericVerticalAlign::TextTop),
+            "middle" => Ok(GenericVerticalAlign::Middle),
+            "bottom" => Ok(GenericVerticalAlign::Bottom),
+            "text-bottom" => Ok(GenericVerticalAlign::TextBottom),
+            #[cfg(feature = "gecko")]
+            "-moz-middle-with-baseline" => {
+                Ok(GenericVerticalAlign::MozMiddleWithBaseline)
+            },
+        }
+    }
+}
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -26,16 +26,17 @@ use values::specified::calc::CalcNode;
 
 pub use properties::animated_properties::TransitionProperty;
 pub use self::angle::Angle;
 #[cfg(feature = "gecko")]
 pub use self::align::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
 pub use self::background::BackgroundSize;
 pub use self::border::{BorderCornerRadius, BorderImageSlice, BorderImageWidth};
 pub use self::border::{BorderImageSideWidth, BorderRadius, BorderSideWidth};
+pub use self::box_::VerticalAlign;
 pub use self::color::{Color, RGBAColor};
 pub use self::effects::{BoxShadow, Filter, SimpleShadow};
 pub use self::flex::FlexBasis;
 #[cfg(feature = "gecko")]
 pub use self::gecko::ScrollSnapPoint;
 pub use self::image::{ColorStop, EndingShape as GradientEndingShape, Gradient};
 pub use self::image::{GradientItem, GradientKind, Image, ImageLayer, MozImageRect};
 pub use self::length::{AbsoluteLength, CalcLengthOrPercentage, CharacterWidth};
@@ -55,16 +56,18 @@ pub use super::generics::grid::GridLine;
 pub use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
 
 #[cfg(feature = "gecko")]
 pub mod align;
 pub mod angle;
 pub mod background;
 pub mod basic_shape;
 pub mod border;
+#[path = "box.rs"]
+pub mod box_;
 pub mod calc;
 pub mod color;
 pub mod effects;
 pub mod flex;
 #[cfg(feature = "gecko")]
 pub mod gecko;
 pub mod grid;
 pub mod image;