layout: Implement box-sizing: border-box.

Improves GitHub.
This commit is contained in:
Patrick Walton 2014-09-26 12:13:34 -07:00
parent f7d2fb6ff8
commit 885fc1c28b
6 changed files with 139 additions and 70 deletions

View file

@ -43,7 +43,7 @@ use std::cmp::{max, min};
use std::fmt; use std::fmt;
use std::mem; use std::mem;
use style::computed_values::{LPA_Auto, LPA_Length, LPA_Percentage, LPN_Length, LPN_None}; use style::computed_values::{LPA_Auto, LPA_Length, LPA_Percentage, LPN_Length, LPN_None};
use style::computed_values::{LPN_Percentage, LP_Length, LP_Percentage}; use style::computed_values::{LPN_Percentage, LP_Length, LP_Percentage, box_sizing};
use style::computed_values::{display, float, overflow}; use style::computed_values::{display, float, overflow};
use sync::Arc; use sync::Arc;
@ -978,14 +978,30 @@ impl BlockFlow {
} }
} }
match self.fragment.style().get_box().box_sizing {
box_sizing::content_box => {
// Adjust `cur_b` as necessary to account for the explicitly-specified block-size. // Adjust `cur_b` as necessary to account for the explicitly-specified block-size.
block_size = candidate_block_size_iterator.candidate_value; block_size = candidate_block_size_iterator.candidate_value;
let delta = block_size - (cur_b - block_start_offset); let delta = block_size - (cur_b - block_start_offset);
translate_including_floats(&mut cur_b, delta, &mut floats); translate_including_floats(&mut cur_b, delta, &mut floats);
// Compute content block-size and noncontent block-size. // Take border and padding into account.
let block_end_offset = self.fragment.border_padding.block_end; let block_end_offset = self.fragment.border_padding.block_end;
translate_including_floats(&mut cur_b, block_end_offset, &mut floats); translate_including_floats(&mut cur_b, block_end_offset, &mut floats);
}
box_sizing::border_box => {
// Adjust `cur_b` as necessary to account for the explicitly-specified block-size.
block_size = candidate_block_size_iterator.candidate_value;
let delta = block_size - cur_b;
translate_including_floats(&mut cur_b, delta, &mut floats);
// Take padding into account.
let block_end_offset = self.fragment.border_padding.block_end -
self.fragment.border_width().block_end;
translate_including_floats(&mut cur_b, block_end_offset, &mut floats);
}
}
// Now that `cur_b` is at the block-end of the border box, compute the final border box // Now that `cur_b` is at the block-end of the border box, compute the final border box
// position. // position.
@ -1203,10 +1219,17 @@ impl BlockFlow {
self.fragment.margin.block_start = solution.margin_block_start; self.fragment.margin.block_start = solution.margin_block_start;
self.fragment.margin.block_end = solution.margin_block_end; self.fragment.margin.block_end = solution.margin_block_end;
self.fragment.border_box.start.b = Au(0); self.fragment.border_box.start.b = Au(0);
self.fragment.border_box.size.block = solution.block_size + self.fragment.border_padding.block_start_end();
self.base.position.start.b = solution.block_start + self.fragment.margin.block_start; self.base.position.start.b = solution.block_start + self.fragment.margin.block_start;
self.base.position.size.block = solution.block_size + self.fragment.border_padding.block_start_end();
let block_size = match self.fragment.style().get_box().box_sizing {
box_sizing::content_box => {
solution.block_size + self.fragment.border_padding.block_start_end()
}
box_sizing::border_box => solution.block_size,
};
self.fragment.border_box.size.block = block_size;
self.base.position.size.block = block_size;
} }
/// Add display items for Absolutely Positioned flow. /// Add display items for Absolutely Positioned flow.
@ -1919,14 +1942,15 @@ pub trait ISizeAndMarginsComputer {
block.static_i_offset()); block.static_i_offset());
} }
/// Set the used values for inline-size and margins got from the relevant constraint equation. /// Set the used values for inline-size and margins from the relevant constraint equation.
///
/// This is called only once. /// This is called only once.
/// ///
/// Set: /// Set:
/// + used values for content inline-size, inline-start margin, and inline-end margin for this flow's box. /// * Used values for content inline-size, inline-start margin, and inline-end margin for this
/// + x-coordinate of this flow's box. /// flow's box;
/// + x-coordinate of the flow wrt its Containing Block (if this is an absolute flow). /// * Inline-start coordinate of this flow's box;
/// * Inline-start coordinate of the flow with respect to its containing block (if this is an
/// absolute flow).
fn set_inline_size_constraint_solutions(&self, fn set_inline_size_constraint_solutions(&self,
block: &mut BlockFlow, block: &mut BlockFlow,
solution: ISizeConstraintSolution) { solution: ISizeConstraintSolution) {
@ -1936,11 +1960,17 @@ pub trait ISizeAndMarginsComputer {
fragment.margin.inline_start = solution.margin_inline_start; fragment.margin.inline_start = solution.margin_inline_start;
fragment.margin.inline_end = solution.margin_inline_end; fragment.margin.inline_end = solution.margin_inline_end;
// The associated fragment has the border box of this flow.
// Left border edge. // Left border edge.
fragment.border_box.start.i = fragment.margin.inline_start; fragment.border_box.start.i = fragment.margin.inline_start;
// Border box inline-size.
inline_size = solution.inline_size + fragment.border_padding.inline_start_end(); // The associated fragment has the border box of this flow.
inline_size = match fragment.style().get_box().box_sizing {
box_sizing::content_box => {
solution.inline_size + fragment.border_padding.inline_start_end()
}
box_sizing::border_box => solution.inline_size,
};
fragment.border_box.size.inline = inline_size; fragment.border_box.size.inline = inline_size;
} }
@ -1966,7 +1996,9 @@ pub trait ISizeAndMarginsComputer {
ctx: &LayoutContext) ctx: &LayoutContext)
-> MaybeAuto { -> MaybeAuto {
MaybeAuto::from_style(block.fragment().style().content_inline_size(), MaybeAuto::from_style(block.fragment().style().content_inline_size(),
self.containing_block_inline_size(block, parent_flow_inline_size, ctx)) self.containing_block_inline_size(block,
parent_flow_inline_size,
ctx))
} }
fn containing_block_inline_size(&self, fn containing_block_inline_size(&self,
@ -1984,15 +2016,21 @@ pub trait ISizeAndMarginsComputer {
block: &mut BlockFlow, block: &mut BlockFlow,
ctx: &LayoutContext, ctx: &LayoutContext,
parent_flow_inline_size: Au) { parent_flow_inline_size: Au) {
let mut input = self.compute_inline_size_constraint_inputs(block, parent_flow_inline_size, ctx); let mut input = self.compute_inline_size_constraint_inputs(block,
parent_flow_inline_size,
ctx);
let containing_block_inline_size = self.containing_block_inline_size(block, parent_flow_inline_size, ctx); let containing_block_inline_size = self.containing_block_inline_size(
block,
parent_flow_inline_size, ctx);
let mut solution = self.solve_inline_size_constraints(block, &input); let mut solution = self.solve_inline_size_constraints(block, &input);
// If the tentative used inline-size is greater than 'max-inline-size', inline-size should be recalculated, // If the tentative used inline-size is greater than 'max-inline-size', inline-size should
// but this time using the computed value of 'max-inline-size' as the computed value for 'inline-size'. // be recalculated, but this time using the computed value of 'max-inline-size' as the
match specified_or_none(block.fragment().style().max_inline_size(), containing_block_inline_size) { // computed value for 'inline-size'.
match specified_or_none(block.fragment().style().max_inline_size(),
containing_block_inline_size) {
Some(max_inline_size) if max_inline_size < solution.inline_size => { Some(max_inline_size) if max_inline_size < solution.inline_size => {
input.computed_inline_size = Specified(max_inline_size); input.computed_inline_size = Specified(max_inline_size);
solution = self.solve_inline_size_constraints(block, &input); solution = self.solve_inline_size_constraints(block, &input);
@ -2000,8 +2038,9 @@ pub trait ISizeAndMarginsComputer {
_ => {} _ => {}
} }
// If the resulting inline-size is smaller than 'min-inline-size', inline-size should be recalculated, // If the resulting inline-size is smaller than 'min-inline-size', inline-size should be
// but this time using the value of 'min-inline-size' as the computed value for 'inline-size'. // recalculated, but this time using the value of 'min-inline-size' as the computed value
// for 'inline-size'.
let computed_min_inline_size = specified(block.fragment().style().min_inline_size(), let computed_min_inline_size = specified(block.fragment().style().min_inline_size(),
containing_block_inline_size); containing_block_inline_size);
if computed_min_inline_size > solution.inline_size { if computed_min_inline_size > solution.inline_size {
@ -2018,13 +2057,15 @@ pub trait ISizeAndMarginsComputer {
/// This is used by both replaced and non-replaced Blocks. /// This is used by both replaced and non-replaced Blocks.
/// ///
/// CSS 2.1 Section 10.3.3. /// CSS 2.1 Section 10.3.3.
/// Constraint Equation: margin-inline-start + margin-inline-end + inline-size = available_inline-size /// Constraint Equation: margin-inline-start + margin-inline-end + inline-size =
/// available_inline-size
/// where available_inline-size = CB inline-size - (horizontal border + padding) /// where available_inline-size = CB inline-size - (horizontal border + padding)
fn solve_block_inline_size_constraints(&self, fn solve_block_inline_size_constraints(&self,
_: &mut BlockFlow, _: &mut BlockFlow,
input: &ISizeConstraintInput) input: &ISizeConstraintInput)
-> ISizeConstraintSolution { -> ISizeConstraintSolution {
let (computed_inline_size, inline_start_margin, inline_end_margin, available_inline_size) = (input.computed_inline_size, let (computed_inline_size, inline_start_margin, inline_end_margin, available_inline_size) =
(input.computed_inline_size,
input.inline_start_margin, input.inline_start_margin,
input.inline_end_margin, input.inline_end_margin,
input.available_inline_size); input.available_inline_size);
@ -2045,20 +2086,25 @@ pub trait ISizeAndMarginsComputer {
} }
}; };
// Invariant: inline-start_margin + inline-size + inline-end_margin == available_inline-size // Invariant: inline-start_margin + inline-size + inline-end_margin ==
let (inline_start_margin, inline_size, inline_end_margin) = match (inline_start_margin, computed_inline_size, inline_end_margin) { // available_inline-size
let (inline_start_margin, inline_size, inline_end_margin) =
match (inline_start_margin, computed_inline_size, inline_end_margin) {
// If all have a computed value other than 'auto', the system is // If all have a computed value other than 'auto', the system is
// over-constrained so we discard the end margin. // over-constrained so we discard the end margin.
(Specified(margin_start), Specified(inline_size), Specified(_margin_end)) => (Specified(margin_start), Specified(inline_size), Specified(_margin_end)) =>
(margin_start, inline_size, available_inline_size - (margin_start + inline_size)), (margin_start, inline_size, available_inline_size -
(margin_start + inline_size)),
// If exactly one value is 'auto', solve for it // If exactly one value is 'auto', solve for it
(Auto, Specified(inline_size), Specified(margin_end)) => (Auto, Specified(inline_size), Specified(margin_end)) =>
(available_inline_size - (inline_size + margin_end), inline_size, margin_end), (available_inline_size - (inline_size + margin_end), inline_size, margin_end),
(Specified(margin_start), Auto, Specified(margin_end)) => (Specified(margin_start), Auto, Specified(margin_end)) =>
(margin_start, available_inline_size - (margin_start + margin_end), margin_end), (margin_start, available_inline_size - (margin_start + margin_end),
margin_end),
(Specified(margin_start), Specified(inline_size), Auto) => (Specified(margin_start), Specified(inline_size), Auto) =>
(margin_start, inline_size, available_inline_size - (margin_start + inline_size)), (margin_start, inline_size, available_inline_size -
(margin_start + inline_size)),
// If inline-size is set to 'auto', any other 'auto' value becomes '0', // If inline-size is set to 'auto', any other 'auto' value becomes '0',
// and inline-size is solved for // and inline-size is solved for

View file

@ -592,11 +592,10 @@ impl Fragment {
text::line_height_from_style(&*self.style, &font_metrics) text::line_height_from_style(&*self.style, &font_metrics)
} }
/// Returns the sum of the inline-sizes of all the borders of this fragment. This is private because /// Returns the sum of the inline-sizes of all the borders of this fragment. Note that this
/// it should only be called during intrinsic inline-size computation or computation of /// can be expensive to compute, so if possible use the `border_padding` field instead.
/// `border_padding`. Other consumers of this information should simply consult that field.
#[inline] #[inline]
fn border_width(&self) -> LogicalMargin<Au> { pub fn border_width(&self) -> LogicalMargin<Au> {
let style_border_width = match self.specific { let style_border_width = match self.specific {
ScannedTextFragment(_) => LogicalMargin::zero(self.style.writing_mode), ScannedTextFragment(_) => LogicalMargin::zero(self.style.writing_mode),
_ => self.style().logical_border_width(), _ => self.style().logical_border_width(),

View file

@ -1075,6 +1075,12 @@ pub mod longhands {
// FIXME(SimonSapin): Add 'mixed' and 'upright' (needs vertical text support) // FIXME(SimonSapin): Add 'mixed' and 'upright' (needs vertical text support)
// FIXME(SimonSapin): initial (first) value should be 'mixed', when that's implemented // FIXME(SimonSapin): initial (first) value should be 'mixed', when that's implemented
${single_keyword("text-orientation", "sideways sideways-left sideways-right", experimental=True)} ${single_keyword("text-orientation", "sideways sideways-left sideways-right", experimental=True)}
// CSS Basic User Interface Module Level 3
// http://dev.w3.org/csswg/css-ui/
${switch_to_style_struct("Box")}
${single_keyword("box-sizing", "content-box border-box")}
} }

View file

@ -162,3 +162,4 @@ fragment=top != ../html/acid2.html acid2_ref.html
== vertical_align_text_top_a.html vertical_align_text_top_ref.html == vertical_align_text_top_a.html vertical_align_text_top_ref.html
== vertical_align_text_bottom_a.html vertical_align_text_bottom_ref.html == vertical_align_text_bottom_a.html vertical_align_text_bottom_ref.html
== inline_hypothetical_box_a.html inline_hypothetical_box_ref.html == inline_hypothetical_box_a.html inline_hypothetical_box_ref.html
== box_sizing_border_box_a.html box_sizing_border_box_ref.html

View file

@ -0,0 +1,8 @@
<!DOCTYPE html>
<html>
<body>
<div style="width: 256px; height: 256px; background: purple; border: 8px solid blue; box-sizing: border-box;"></div>
<div style="width: 240px; height: 240px; background: purple; border: 8px solid blue;"></div>
</body>
</html>

View file

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<body>
<div style="width: 240px; height: 240px; background: purple; border: 8px solid blue;"></div>
<div style="width: 240px; height: 240px; background: purple; border: 8px solid blue;"></div>
</body>
</html>