servo: Merge #602 - Properly account for relative CSS units in borders, margins, padding, an (from metajack:relative-bpm); r=pcwalton
authorJack Moffitt <jack@metajack.im>
Thu, 18 Jul 2013 19:15:38 -0700
changeset 333526 12499f24aff5f4b581f995146a0a65cf0681467f
parent 333525 c7c219b8a51b91f992466af4572b7df44a9b0e33
child 333527 9c9bc8204ea451eaabaf9a029361258e1352aa46
push id31307
push usergszorc@mozilla.com
push dateSat, 04 Feb 2017 00:59:06 +0000
treeherdermozilla-central@94079d43835f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspcwalton
servo: Merge #602 - Properly account for relative CSS units in borders, margins, padding, an (from metajack:relative-bpm); r=pcwalton ...d widths. Source-Repo: https://github.com/servo/servo Source-Revision: b68b573d77e649b582d6fad18f6be93477aabc80
servo/src/components/main/layout/block.rs
servo/src/components/main/layout/box.rs
servo/src/components/main/layout/float.rs
servo/src/components/main/layout/model.rs
--- a/servo/src/components/main/layout/block.rs
+++ b/servo/src/components/main/layout/block.rs
@@ -129,18 +129,18 @@ impl BlockFlowData {
                             left_margin: MaybeAuto, 
                             right_margin: MaybeAuto, 
                             available_width: Au) -> (Au, Au, Au) {
 
         //If width is not 'auto', and width + margins > available_width, all 'auto' margins are treated as '0'
         let (left_margin, right_margin) = match width{
             Auto => (left_margin, right_margin),
             Specified(width) => {
-                let left = left_margin.spec_or_default(Au(0));
-                let right = right_margin.spec_or_default(Au(0));
+                let left = left_margin.specified_or_zero();
+                let right = right_margin.specified_or_zero();
                 
                 if((left + right + width) > available_width) {
                     (Specified(left), Specified(right))
                 } else {
                     (left_margin, right_margin)
                 }
             }
         };
@@ -197,24 +197,26 @@ impl BlockFlowData {
                 // Can compute padding here since we know containing block width.
                 model.compute_padding(style, remaining_width);
 
                 // Margins are 0 right now so model.noncontent_width() is just borders + padding.
                 let available_width = remaining_width - model.noncontent_width();
 
                 // Top and bottom margins for blocks are 0 if auto.
                 let margin_top = MaybeAuto::from_margin(style.margin_top(),
-                                                        remaining_width).spec_or_default(Au(0));
+                                                        remaining_width,
+                                                        style.font_size()).specified_or_zero();
                 let margin_bottom = MaybeAuto::from_margin(style.margin_bottom(),
-                                                           remaining_width).spec_or_default(Au(0));
+                                                           remaining_width,
+                                                           style.font_size()).specified_or_zero();
 
                 let (width, margin_left, margin_right) =
-                    (MaybeAuto::from_width(style.width(), remaining_width),
-                     MaybeAuto::from_margin(style.margin_left(), remaining_width),
-                     MaybeAuto::from_margin(style.margin_right(), remaining_width));
+                    (MaybeAuto::from_width(style.width(), remaining_width, style.font_size()),
+                     MaybeAuto::from_margin(style.margin_left(), remaining_width, style.font_size()),
+                     MaybeAuto::from_margin(style.margin_right(), remaining_width, style.font_size()));
 
                 let (width, margin_left, margin_right) = self.compute_horiz(width,
                                                                             margin_left,
                                                                             margin_right,
                                                                             available_width);
 
                 model.margin.top = margin_top;
                 model.margin.right = margin_right;
@@ -292,18 +294,18 @@ impl BlockFlowData {
         let mut height = if self.is_root {
             Au::max(ctx.screen_size.size.height, cur_y)
         } else {
                 cur_y - top_offset
         };
 
         for self.box.iter().advance |&box| {
             let style = box.style();
-            let maybe_height = MaybeAuto::from_height(style.height(), Au(0));
-            let maybe_height = maybe_height.spec_or_default(Au(0));
+            let maybe_height = MaybeAuto::from_height(style.height(), Au(0), style.font_size());
+            let maybe_height = maybe_height.specified_or_zero();
             height = geometry::max(height, maybe_height);
         }
 
         let mut noncontent_height = Au(0);
         self.box.map(|&box| {
             do box.with_mut_base |base| {
                 //The associated box is the border box of this flow
                 base.position.origin.y = base.model.margin.top;
--- a/servo/src/components/main/layout/box.rs
+++ b/servo/src/components/main/layout/box.rs
@@ -383,26 +383,40 @@ impl RenderBox {
     //
     // TODO(eatkinson): integrate with
     // get_min_width and get_pref_width?
     priv fn guess_width (&self) -> Au {
         do self.with_base |base| {
             if(!base.node.is_element()) {
                 Au(0)
             } else {
+                let style = self.style();
+                let font_size = style.font_size();
+                let width = MaybeAuto::from_width(style.width(),
+                                                  Au(0),
+                                                  font_size).specified_or_zero();
+                let margin_left = MaybeAuto::from_margin(style.margin_left(),
+                                                         Au(0),
+                                                         font_size).specified_or_zero();
+                let margin_right = MaybeAuto::from_margin(style.margin_right(),
+                                                          Au(0),
+                                                          font_size).specified_or_zero();
+                let padding_left = base.model.compute_padding_length(style.padding_left(),
+                                                                     Au(0),
+                                                                     font_size);
+                let padding_right = base.model.compute_padding_length(style.padding_right(),
+                                                                      Au(0),
+                                                                      font_size);
+                let border_left = base.model.compute_border_width(style.border_left_width(),
+                                                                  font_size);
+                let border_right = base.model.compute_border_width(style.border_right_width(),
+                                                                   font_size);
 
-                let w = MaybeAuto::from_width(self.style().width(), Au(0)).spec_or_default(Au(0));
-                let ml = MaybeAuto::from_margin(self.style().margin_left(), Au(0)).spec_or_default(Au(0));
-                let mr = MaybeAuto::from_margin(self.style().margin_right(), Au(0)).spec_or_default(Au(0));
-                let pl = base.model.compute_padding_length(self.style().padding_left(), Au(0));
-                let pr = base.model.compute_padding_length(self.style().padding_right(), Au(0));
-                let bl = base.model.compute_border_width(self.style().border_left_width());
-                let br = base.model.compute_border_width(self.style().border_right_width());
-
-                w + ml + mr + pl + pr + bl + br
+                width + margin_left + margin_right + padding_left + padding_right + 
+                    border_left + border_right
             }
         }
     }
 
     /// Returns the *minimum width* of this render box as defined by the CSS specification.
     pub fn get_min_width(&self, _: &LayoutContext) -> Au {
         // FIXME(pcwalton): I think we only need to calculate this if the damage says that CSS
         // needs to be restyled.
--- a/servo/src/components/main/layout/float.rs
+++ b/servo/src/components/main/layout/float.rs
@@ -101,33 +101,38 @@ impl FloatFlowData {
         for self.box.iter().advance |&box| {
             let style = box.style();
             do box.with_model |model| {
                 // Can compute padding here since we know containing block width.
                 model.compute_padding(style, remaining_width);
 
                 // Margins for floats are 0 if auto.
                 let margin_top = MaybeAuto::from_margin(style.margin_top(),
-                                                        remaining_width).spec_or_default(Au(0));
+                                                        remaining_width,
+                                                        style.font_size()).specified_or_zero();
                 let margin_bottom = MaybeAuto::from_margin(style.margin_bottom(),
-                                                           remaining_width).spec_or_default(Au(0));
+                                                           remaining_width,
+                                                           style.font_size()).specified_or_zero();
                 let margin_left = MaybeAuto::from_margin(style.margin_left(),
-                                                        remaining_width).spec_or_default(Au(0));
+                                                         remaining_width,
+                                                         style.font_size()).specified_or_zero();
                 let margin_right = MaybeAuto::from_margin(style.margin_right(),
-                                                           remaining_width).spec_or_default(Au(0));
+                                                          remaining_width,
+                                                          style.font_size()).specified_or_zero();
 
 
 
                 let shrink_to_fit = geometry::min(self.common.pref_width, 
                                                   geometry::max(self.common.min_width, 
                                                                 remaining_width));
 
 
                 let width = MaybeAuto::from_width(style.width(), 
-                                                  remaining_width).spec_or_default(shrink_to_fit);
+                                                  remaining_width,
+                                                  style.font_size()).specified_or_default(shrink_to_fit);
                 debug!("assign_widths_float -- width: %?", width);
 
                 model.margin.top = margin_top;
                 model.margin.right = margin_right;
                 model.margin.bottom = margin_bottom;
                 model.margin.left = margin_left;
 
                 x_offset = model.offset();
@@ -192,19 +197,20 @@ impl FloatFlowData {
 
                 noncontent_height = noncontent_height + base.model.margin.top + base.model.margin.bottom;
             }
         });
 
         
         //TODO(eatkinson): compute heights properly using the 'height' property.
         for self.box.iter().advance |&box| {
-
             let height_prop = 
-                MaybeAuto::from_height(box.style().height(), Au(0)).spec_or_default(Au(0));
+                MaybeAuto::from_height(box.style().height(),
+                                       Au(0),
+                                       box.style().font_size()).specified_or_zero();
 
             height = geometry::max(height, height_prop) + noncontent_height;
             debug!("assign_height_float -- height: %?", height);
             do box.with_mut_base |base| {
                 base.position.size.height = height;
             }
         }
 
--- a/servo/src/components/main/layout/model.rs
+++ b/servo/src/components/main/layout/model.rs
@@ -5,17 +5,17 @@
 //! Borders, padding, and margins.
 
 use std::num::Zero;
 use geom::side_offsets::SideOffsets2D;
 use gfx::geometry::Au;
 use newcss::complete::CompleteStyle;
 use newcss::units::{Em, Pt, Px};
 use newcss::values::{CSSBorderWidth, CSSBorderWidthLength, CSSBorderWidthMedium};
-use newcss::values::{CSSBorderWidthThick, CSSBorderWidthThin};
+use newcss::values::{CSSBorderWidthThick, CSSBorderWidthThin, CSSFontSize, CSSFontSizeLength};
 use newcss::values::{CSSWidth, CSSWidthLength, CSSWidthPercentage, CSSWidthAuto};
 use newcss::values::{CSSHeight, CSSHeightLength, CSSHeightPercentage, CSSHeightAuto};
 use newcss::values::{CSSMargin, CSSMarginLength, CSSMarginPercentage, CSSMarginAuto};
 use newcss::values::{CSSPadding, CSSPaddingLength, CSSPaddingPercentage};
 /// Encapsulates the borders, padding, and margins, which we collectively call the "box model".
 pub struct BoxModel {
     border: SideOffsets2D<Au>,
     padding: SideOffsets2D<Au>,
@@ -25,57 +25,76 @@ pub struct BoxModel {
 }
 
 /// Useful helper data type when computing values for blocks and positioned elements.
 pub enum MaybeAuto {
     Auto,
     Specified(Au),
 }
 
-impl MaybeAuto{
-    pub fn from_margin(margin: CSSMargin, cb_width: Au) -> MaybeAuto{
+impl MaybeAuto {
+    pub fn from_margin(margin: CSSMargin, containing_width: Au, font_size: CSSFontSize) -> MaybeAuto {
         match margin {
             CSSMarginAuto => Auto,
-            //FIXME(eatkinson): Compute percents properly
-            CSSMarginPercentage(percent) => Specified(cb_width.scale_by(percent/100.0)),
-            //FIXME(eatkinson): Compute pt and em values properly
-            CSSMarginLength(Px(v)) | 
-            CSSMarginLength(Pt(v)) | 
-            CSSMarginLength(Em(v)) => Specified(Au::from_frac_px(v)),
+            CSSMarginPercentage(percent) => Specified(containing_width.scale_by(percent/100.0)),
+            CSSMarginLength(Px(v)) => Specified(Au::from_frac_px(v)),
+            CSSMarginLength(Pt(v)) => Specified(Au::from_pt(v)),
+            CSSMarginLength(Em(em)) => {
+                match font_size {
+                    CSSFontSizeLength(Px(v)) => Specified(Au::from_frac_px(em * v)),
+                    CSSFontSizeLength(Pt(v)) => Specified(Au::from_pt(em * v)),
+                    _ => fail!(~"expected non-relative font size"),
+                }
+            }
         }
     }
 
-    pub fn from_width(width: CSSWidth, cb_width: Au) -> MaybeAuto{
-        match width{
+    pub fn from_width(width: CSSWidth, containing_width: Au, font_size: CSSFontSize) -> MaybeAuto {
+        match width {
             CSSWidthAuto => Auto,
-            CSSWidthPercentage(percent) => Specified(cb_width.scale_by(percent/100.0)),
-            //FIXME(eatkinson): Compute pt and em values properly
-            CSSWidthLength(Px(v)) | 
-            CSSWidthLength(Pt(v)) | 
-            CSSWidthLength(Em(v)) => Specified(Au::from_frac_px(v)),
+            CSSWidthPercentage(percent) => Specified(containing_width.scale_by(percent/100.0)),
+            CSSWidthLength(Px(v)) => Specified(Au::from_frac_px(v)),
+            CSSWidthLength(Pt(v)) => Specified(Au::from_pt(v)),
+            CSSWidthLength(Em(em)) => {
+                match font_size {
+                    CSSFontSizeLength(Px(v)) => Specified(Au::from_frac_px(em * v)),
+                    CSSFontSizeLength(Pt(v)) => Specified(Au::from_pt(em * v)),
+                    _ => fail!(~"expected non-relative font size"),
+                }
+            }
         }
     }
 
-    pub fn from_height(height: CSSHeight, cb_height: Au) -> MaybeAuto{
+    pub fn from_height(height: CSSHeight, cb_height: Au, font_size: CSSFontSize) -> MaybeAuto {
         match height {
             CSSHeightAuto => Auto,
             CSSHeightPercentage(percent) => Specified(cb_height.scale_by(percent/100.0)),
-            //FIXME(eatkinson): Compute pt and em values properly
-            CSSHeightLength(Px(v)) | 
-            CSSHeightLength(Pt(v)) | 
-            CSSHeightLength(Em(v)) => Specified(Au::from_frac_px(v)),
+            CSSHeightLength(Px(v)) => Specified(Au::from_frac_px(v)),
+            CSSHeightLength(Pt(v)) => Specified(Au::from_pt(v)),
+            CSSHeightLength(Em(em)) => {
+                match font_size {
+                    CSSFontSizeLength(Px(v)) => Specified(Au::from_frac_px(em * v)),
+                    CSSFontSizeLength(Pt(v)) => Specified(Au::from_pt(em * v)),
+                    _ => fail!(~"expected non-relative font size"),
+                }
+            }
+
         }
     }
 
-    pub fn spec_or_default(&self, default: Au) -> Au{
-        match *self{
+    pub fn specified_or_default(&self, default: Au) -> Au {
+        match *self {
             Auto => default,
-            Specified(value) => value
+            Specified(value) => value,
         }
     }
+
+    pub fn specified_or_zero(&self) -> Au {
+        self.specified_or_default(Au(0))
+    }
 }
 
 impl Zero for BoxModel {
     fn zero() -> BoxModel {
         BoxModel {
             border: Zero::zero(),
             padding: Zero::zero(),
             margin: Zero::zero(),
@@ -87,58 +106,64 @@ impl Zero for BoxModel {
         self.padding.is_zero() && self.border.is_zero() && self.margin.is_zero()
     }
 }
 
 impl BoxModel {
     /// Populates the box model parameters from the given computed style.
     pub fn compute_borders(&mut self, style: CompleteStyle) {
         // Compute the borders.
-        self.border.top = self.compute_border_width(style.border_top_width());
-        self.border.right = self.compute_border_width(style.border_right_width());
-        self.border.bottom = self.compute_border_width(style.border_bottom_width());
-        self.border.left = self.compute_border_width(style.border_left_width());
+        self.border.top = self.compute_border_width(style.border_top_width(), style.font_size());
+        self.border.right = self.compute_border_width(style.border_right_width(), style.font_size());
+        self.border.bottom = self.compute_border_width(style.border_bottom_width(), style.font_size());
+        self.border.left = self.compute_border_width(style.border_left_width(), style.font_size());
     }
 
-    pub fn compute_padding(&mut self, style: CompleteStyle, cb_width: Au){
-        self.padding.top = self.compute_padding_length(style.padding_top(), cb_width);
-        self.padding.right = self.compute_padding_length(style.padding_right(), cb_width);
-        self.padding.bottom = self.compute_padding_length(style.padding_bottom(), cb_width);
-        self.padding.left = self.compute_padding_length(style.padding_left(), cb_width);
+    pub fn compute_padding(&mut self, style: CompleteStyle, containing_width: Au) {
+        self.padding.top = self.compute_padding_length(style.padding_top(), containing_width, style.font_size());
+        self.padding.right = self.compute_padding_length(style.padding_right(), containing_width, style.font_size());
+        self.padding.bottom = self.compute_padding_length(style.padding_bottom(), containing_width, style.font_size());
+        self.padding.left = self.compute_padding_length(style.padding_left(), containing_width, style.font_size());
     }
 
     pub fn noncontent_width(&self) -> Au {
         let left = self.margin.left + self.border.left + self.padding.left;
         let right = self.margin.right + self.border.right + self.padding.right;
         left + right
     }
 
     pub fn offset(&self) -> Au {
         self.margin.left + self.border.left + self.padding.left
     }
 
     /// Helper function to compute the border width in app units from the CSS border width.
-    pub fn compute_border_width(&self, width: CSSBorderWidth) -> Au {
+    pub fn compute_border_width(&self, width: CSSBorderWidth, font_size: CSSFontSize) -> Au {
         match width {
-            CSSBorderWidthLength(Px(v)) |
-            CSSBorderWidthLength(Em(v)) |
-            CSSBorderWidthLength(Pt(v)) => {
-                // FIXME(pcwalton): Handle `em` and `pt` correctly.
-                Au::from_frac_px(v)
-            }
+            CSSBorderWidthLength(Px(v)) => Au::from_frac_px(v),
+            CSSBorderWidthLength(Pt(v)) => Au::from_pt(v),
+            CSSBorderWidthLength(Em(em)) => {
+                match font_size {
+                    CSSFontSizeLength(Px(v)) => Au::from_frac_px(em * v),
+                    CSSFontSizeLength(Pt(v)) => Au::from_pt(em * v),
+                    _ => fail!(~"expected non-relative font size"),
+                }
+            },
             CSSBorderWidthThin => Au::from_px(1),
             CSSBorderWidthMedium => Au::from_px(5),
             CSSBorderWidthThick => Au::from_px(10),
         }
     }
 
-    pub fn compute_padding_length(&self, padding: CSSPadding, content_box_width: Au) -> Au {
+    pub fn compute_padding_length(&self, padding: CSSPadding, content_box_width: Au, font_size: CSSFontSize) -> Au {
         match padding {
-            CSSPaddingLength(Px(v)) |
-            CSSPaddingLength(Pt(v)) |
-            CSSPaddingLength(Em(v)) => {
-                // FIXME(eatkinson): Handle 'em' and 'pt' correctly
-                Au::from_frac_px(v)
-            }
+            CSSPaddingLength(Px(v)) => Au::from_frac_px(v),
+            CSSPaddingLength(Pt(v)) => Au::from_pt(v),
+            CSSPaddingLength(Em(em)) => {
+                match font_size {
+                    CSSFontSizeLength(Px(v)) => Au::from_frac_px(em * v),
+                    CSSFontSizeLength(Pt(v)) => Au::from_pt(em * v),
+                    _ => fail!(~"expected non-relative font size"),
+                }
+            },
             CSSPaddingPercentage(p) => content_box_width.scale_by(p/100.0)
         }
     }
 }