layout: Implement most of border-collapse per CSS 2.1 § 17.6.2.

Known issues:

* Collapsed borders do not correctly affect the border-box of the table
  itself.

* The content widths of all cells in a column and the content height of
  all cells in a row is the same in this patch, but not in Gecko and
  WebKit.

* Corners are not painted well. The spec does not say what to do here.

* Column spans are not handled well. The spec does not say what to do
  here either.
This commit is contained in:
Patrick Walton 2015-03-09 12:33:04 -07:00 committed by Simon Sapin
parent 92359c7b9a
commit 48299a53cb
22 changed files with 1975 additions and 520 deletions

View file

@ -171,13 +171,27 @@ impl DisplayList {
let doit = |items: &Vec<DisplayItem>| {
for item in items.iter() {
match *item {
DisplayItem::SolidColorClass(ref solid_color) => println!("{} SolidColor. {:?}", indentation, solid_color.base.bounds),
DisplayItem::TextClass(ref text) => println!("{:?} Text. {:?}", indentation, text.base.bounds),
DisplayItem::ImageClass(ref image) => println!("{:?} Image. {:?}", indentation, image.base.bounds),
DisplayItem::BorderClass(ref border) => println!("{:?} Border. {:?}", indentation, border.base.bounds),
DisplayItem::GradientClass(ref gradient) => println!("{:?} Gradient. {:?}", indentation, gradient.base.bounds),
DisplayItem::LineClass(ref line) => println!("{:?} Line. {:?}", indentation, line.base.bounds),
DisplayItem::BoxShadowClass(ref box_shadow) => println!("{:?} Box_shadow. {:?}", indentation, box_shadow.base.bounds),
DisplayItem::SolidColorClass(ref solid_color) => {
println!("{:?} SolidColor. {:?}", indentation, solid_color.base.bounds)
}
DisplayItem::TextClass(ref text) => {
println!("{:?} Text. {:?}", indentation, text.base.bounds)
}
DisplayItem::ImageClass(ref image) => {
println!("{:?} Image. {:?}", indentation, image.base.bounds)
}
DisplayItem::BorderClass(ref border) => {
println!("{:?} Border. {:?}", indentation, border.base.bounds)
}
DisplayItem::GradientClass(ref gradient) => {
println!("{:?} Gradient. {:?}", indentation, gradient.base.bounds)
}
DisplayItem::LineClass(ref line) => {
println!("{:?} Line. {:?}", indentation, line.base.bounds)
}
DisplayItem::BoxShadowClass(ref box_shadow) => {
println!("{:?} Box_shadow. {:?}", indentation, box_shadow.base.bounds)
}
}
}
println!("\n");

View file

@ -290,11 +290,7 @@ impl<'a> PaintContext<'a> {
radii: &BorderRadii<AzFloat>,
color: Color) {
let mut path_builder = self.draw_target.create_path_builder();
self.create_border_path_segment(&mut path_builder,
bounds,
direction,
border,
radii);
self.create_border_path_segment(&mut path_builder, bounds, direction, border, radii);
let draw_options = DrawOptions::new(1.0, 0);
self.draw_target.fill(&path_builder.finish(), &ColorPattern::new(color), &draw_options);
}
@ -693,7 +689,8 @@ impl<'a> PaintContext<'a> {
let scaled_left_top = left_top + Point2D(scaled_border.left,
scaled_border.top);
return Rect(scaled_left_top,
Size2D(rect.size.width - 2.0 * scaled_border.right, rect.size.height - 2.0 * scaled_border.bottom));
Size2D(rect.size.width - 2.0 * scaled_border.right,
rect.size.height - 2.0 * scaled_border.bottom));
}
fn scale_color(&self, color: Color, scale_factor: f32) -> Color {
@ -1129,6 +1126,7 @@ impl ToAzureRect for Rect<Au> {
fn to_azure_rect(&self) -> Rect<AzFloat> {
Rect(self.origin.to_azure_point(), Size2D(self.size.width.to_nearest_px() as AzFloat,
self.size.height.to_nearest_px() as AzFloat))
}
fn to_subpx_azure_rect(&self) -> Rect<AzFloat> {
Rect(self.origin.to_subpx_azure_point(), Size2D(self.size.width.to_subpx() as AzFloat,

View file

@ -29,7 +29,8 @@
use context::LayoutContext;
use css::node_style::StyledNode;
use display_list_builder::{BlockFlowDisplayListBuilding, FragmentDisplayListBuilding};
use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode};
use display_list_builder::{FragmentDisplayListBuilding};
use floats::{ClearType, FloatKind, Floats, PlacementInfo};
use flow::{self, AbsolutePositionInfo, BaseFlow, ForceNonfloatedFlag, FlowClass, Flow};
use flow::{ImmutableFlowUtils, MutableFlowUtils, PreorderFlowTraversal};
@ -44,7 +45,6 @@ use incremental::{REFLOW, REFLOW_OUT_OF_FLOW};
use layout_debug;
use model::{IntrinsicISizes, MarginCollapseInfo};
use model::{MaybeAuto, CollapsibleMargins, specified, specified_or_none};
use table;
use wrapper::ThreadSafeLayoutNode;
use geom::{Point2D, Rect, Size2D};
@ -54,12 +54,13 @@ use rustc_serialize::{Encoder, Encodable};
use std::cmp::{max, min};
use std::fmt;
use std::sync::Arc;
use style::computed_values::{overflow_x, overflow_y, position, box_sizing, display, float};
use style::computed_values::{border_collapse, box_sizing, display, float, overflow_x, overflow_y};
use style::computed_values::{position};
use style::properties::ComputedValues;
use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};
use style::values::computed::{LengthOrPercentageOrNone};
use util::geometry::{Au, MAX_AU};
use util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize};
use util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize, WritingMode};
use util::opts;
/// Information specific to floated blocks.
@ -621,44 +622,51 @@ impl BlockFlow {
/// Compute the actual inline size and position for this block.
pub fn compute_used_inline_size(&mut self,
layout_context: &LayoutContext,
containing_block_inline_size: Au) {
containing_block_inline_size: Au,
border_collapse: border_collapse::T) {
let block_type = self.block_type();
match block_type {
BlockType::AbsoluteReplaced => {
let inline_size_computer = AbsoluteReplaced;
inline_size_computer.compute_used_inline_size(self,
layout_context,
containing_block_inline_size);
containing_block_inline_size,
border_collapse);
}
BlockType::AbsoluteNonReplaced => {
let inline_size_computer = AbsoluteNonReplaced;
inline_size_computer.compute_used_inline_size(self,
layout_context,
containing_block_inline_size);
containing_block_inline_size,
border_collapse);
}
BlockType::FloatReplaced => {
let inline_size_computer = FloatReplaced;
inline_size_computer.compute_used_inline_size(self,
layout_context,
containing_block_inline_size);
containing_block_inline_size,
border_collapse);
}
BlockType::FloatNonReplaced => {
let inline_size_computer = FloatNonReplaced;
inline_size_computer.compute_used_inline_size(self,
layout_context,
containing_block_inline_size);
containing_block_inline_size,
border_collapse);
}
BlockType::Replaced => {
let inline_size_computer = BlockReplaced;
inline_size_computer.compute_used_inline_size(self,
layout_context,
containing_block_inline_size);
containing_block_inline_size,
border_collapse);
}
BlockType::NonReplaced => {
let inline_size_computer = BlockNonReplaced;
inline_size_computer.compute_used_inline_size(self,
layout_context,
containing_block_inline_size);
containing_block_inline_size,
border_collapse);
}
}
}
@ -1193,27 +1201,37 @@ impl BlockFlow {
/// Compute inline size based using the `block_container_inline_size` set by the parent flow.
///
/// This is run in the `AssignISizes` traversal.
pub fn propagate_and_compute_used_inline_size(&mut self, layout_context: &LayoutContext) {
fn propagate_and_compute_used_inline_size(&mut self,
layout_context: &LayoutContext,
border_collapse: border_collapse::T) {
let containing_block_inline_size = self.base.block_container_inline_size;
self.compute_used_inline_size(layout_context, containing_block_inline_size);
self.compute_used_inline_size(layout_context,
containing_block_inline_size,
border_collapse);
if self.base.flags.is_float() {
self.float.as_mut().unwrap().containing_inline_size = containing_block_inline_size;
self.float.as_mut().unwrap().containing_inline_size = containing_block_inline_size
}
}
/// Assigns the computed inline-start content edge and inline-size to all the children of this
/// block flow. Also computes whether each child will be impacted by floats.
/// block flow. Also computes whether each child will be impacted by floats. The given
/// `callback`, if supplied, will be called once per child; it is currently used to push down
/// column sizes for tables.
///
/// `#[inline(always)]` because this is called only from block or table inline-size assignment
/// and the code for block layout is significantly simpler.
#[inline(always)]
pub fn propagate_assigned_inline_size_to_children(
&mut self,
layout_context: &LayoutContext,
inline_start_content_edge: Au,
inline_end_content_edge: Au,
content_inline_size: Au,
table_info: Option<table::ChildInlineSizeInfo>) {
pub fn propagate_assigned_inline_size_to_children<F>(&mut self,
layout_context: &LayoutContext,
inline_start_content_edge: Au,
inline_end_content_edge: Au,
content_inline_size: Au,
mut callback: F)
where F: FnMut(&mut Flow,
usize,
Au,
WritingMode,
&mut Au) {
// Keep track of whether floats could impact each child.
let mut inline_start_floats_impact_child =
self.base.flags.contains(IMPACTED_BY_LEFT_FLOATS);
@ -1277,13 +1295,10 @@ impl BlockFlow {
let containing_block_mode = self.base.writing_mode;
// This value is used only for table cells.
let mut inline_start_margin_edge = if table_info.is_some() {
inline_start_content_edge
} else {
Au(0)
};
let mut inline_start_margin_edge = inline_start_content_edge;
for (i, kid) in self.base.child_iter().enumerate() {
let mut iterator = self.base.child_iter().enumerate().peekable();
while let Some((i, kid)) = iterator.next() {
{
let kid_base = flow::mut_base(kid);
kid_base.block_container_explicit_block_size = explicit_content_size;
@ -1350,14 +1365,13 @@ impl BlockFlow {
inline_size_of_preceding_right_floats;
}
// Handle tables.
if let Some(ref table_info) = table_info {
table_info.propagate_to_child(kid,
i,
content_inline_size,
containing_block_mode,
&mut inline_start_margin_edge);
}
// Call the callback to propagate extra inline size information down to the child. This
// is currently used for tables.
callback(kid,
i,
content_inline_size,
containing_block_mode,
&mut inline_start_margin_edge);
// Per CSS 2.1 § 16.3.1, text alignment propagates to all children in flow.
//
@ -1575,7 +1589,7 @@ impl Flow for BlockFlow {
// Our inline-size was set to the inline-size of the containing block by the flow's parent.
// Now compute the real value.
self.propagate_and_compute_used_inline_size(layout_context);
self.propagate_and_compute_used_inline_size(layout_context, border_collapse::T::separate);
// Formatting contexts are never impacted by floats.
match self.formatting_context_type() {
@ -1617,7 +1631,7 @@ impl Flow for BlockFlow {
inline_start_content_edge,
inline_end_content_edge,
content_inline_size,
None);
|_, _, _, _, _| {});
}
fn place_float_if_applicable<'a>(&mut self, _: &'a LayoutContext<'a>) {
@ -1906,7 +1920,9 @@ impl Flow for BlockFlow {
}
fn build_display_list(&mut self, layout_context: &LayoutContext) {
self.build_display_list_for_block(box DisplayList::new(), layout_context);
self.build_display_list_for_block(box DisplayList::new(),
layout_context,
BorderPaintingMode::Separate);
if opts::get().validate_display_list_geometry {
self.base.validate_display_list_geometry();
}
@ -2031,14 +2047,15 @@ pub trait ISizeAndMarginsComputer {
fn compute_inline_size_constraint_inputs(&self,
block: &mut BlockFlow,
parent_flow_inline_size: Au,
layout_context: &LayoutContext)
layout_context: &LayoutContext,
border_collapse: border_collapse::T)
-> ISizeConstraintInput {
let containing_block_inline_size =
self.containing_block_inline_size(block, parent_flow_inline_size, layout_context);
block.fragment.compute_block_direction_margins(containing_block_inline_size);
block.fragment.compute_inline_direction_margins(containing_block_inline_size);
block.fragment.compute_border_and_padding(containing_block_inline_size);
block.fragment.compute_border_and_padding(containing_block_inline_size, border_collapse);
let mut computed_inline_size = self.initial_computed_inline_size(block,
parent_flow_inline_size,
@ -2165,10 +2182,12 @@ pub trait ISizeAndMarginsComputer {
fn compute_used_inline_size(&self,
block: &mut BlockFlow,
layout_context: &LayoutContext,
parent_flow_inline_size: Au) {
parent_flow_inline_size: Au,
border_collapse: border_collapse::T) {
let mut input = self.compute_inline_size_constraint_inputs(block,
parent_flow_inline_size,
layout_context);
layout_context,
border_collapse);
let containing_block_inline_size =
self.containing_block_inline_size(block, parent_flow_inline_size, layout_context);
@ -2617,7 +2636,9 @@ impl ISizeAndMarginsComputer for BlockReplaced {
-> ISizeConstraintSolution {
match input.computed_inline_size {
MaybeAuto::Specified(_) => {},
MaybeAuto::Auto => panic!("BlockReplaced: inline_size should have been computed by now")
MaybeAuto::Auto => {
panic!("BlockReplaced: inline_size should have been computed by now")
}
};
self.solve_block_inline_size_constraints(block, input)
}

View file

@ -20,6 +20,7 @@ use fragment::{ScannedTextFragmentInfo, SpecificFragmentInfo};
use inline::InlineFlow;
use list_item::ListItemFlow;
use model::{self, MaybeAuto, ToGfxMatrix};
use table_cell::CollapsedBordersForCell;
use geom::{Matrix2D, Point2D, Rect, Size2D, SideOffsets2D};
use gfx::color;
@ -31,31 +32,31 @@ use gfx::display_list::{GradientStop, ImageDisplayItem, LineDisplayItem};
use gfx::display_list::{OpaqueNode, SolidColorDisplayItem};
use gfx::display_list::{StackingContext, TextDisplayItem, TextOrientation};
use gfx::paint_task::{PaintLayer, THREAD_TINT_COLORS};
use png::{self, PixelsByColorType};
use msg::compositor_msg::ScrollPolicy;
use msg::constellation_msg::Msg as ConstellationMsg;
use msg::constellation_msg::ConstellationChan;
use util::cursor::Cursor;
use util::geometry::{self, Au, ZERO_POINT, to_px, to_frac_px};
use util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize, WritingMode};
use util::opts;
use msg::constellation_msg::Msg as ConstellationMsg;
use png::{self, PixelsByColorType};
use std::cmp;
use std::default::Default;
use std::iter::repeat;
use std::num::Float;
use style::values::specified::{AngleOrCorner, HorizontalDirection, VerticalDirection};
use style::values::computed::{Image, LinearGradient, LengthOrPercentage, LengthOrPercentageOrAuto};
use style::values::RGBA;
use std::num::ToPrimitive;
use std::sync::Arc;
use std::sync::mpsc::channel;
use style::computed_values::filter::Filter;
use style::computed_values::transform::ComputedMatrix;
use style::computed_values::{background_attachment, background_repeat, background_size};
use style::computed_values::{border_style, image_rendering, overflow_x, position, visibility};
use style::properties::style_structs::Border;
use style::properties::ComputedValues;
use std::num::ToPrimitive;
use std::sync::Arc;
use std::sync::mpsc::channel;
use style::properties::style_structs::Border;
use style::values::RGBA;
use style::values::computed::{Image, LinearGradient, LengthOrPercentage, LengthOrPercentageOrAuto};
use style::values::specified::{AngleOrCorner, HorizontalDirection, VerticalDirection};
use url::Url;
use util::cursor::Cursor;
use util::geometry::{self, Au, ZERO_POINT, to_px, to_frac_px};
use util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize, WritingMode};
use util::opts;
/// The results of display list building for a single flow.
pub enum DisplayListBuildingResult {
@ -123,12 +124,14 @@ pub trait FragmentDisplayListBuilding {
/// Adds the display items necessary to paint the borders of this fragment to a display list if
/// necessary.
fn build_display_list_for_borders_if_applicable(&self,
style: &ComputedValues,
display_list: &mut DisplayList,
abs_bounds: &Rect<Au>,
level: StackingLevel,
clip: &ClippingRegion);
fn build_display_list_for_borders_if_applicable(
&self,
style: &ComputedValues,
border_painting_mode: BorderPaintingMode,
display_list: &mut DisplayList,
bounds: &Rect<Au>,
level: StackingLevel,
clip: &ClippingRegion);
/// Adds the display items necessary to paint the outline of this fragment to the display list
/// if necessary.
@ -181,6 +184,7 @@ pub trait FragmentDisplayListBuilding {
stacking_relative_flow_origin: &Point2D<Au>,
relative_containing_block_size: &LogicalSize<Au>,
relative_containing_block_mode: WritingMode,
border_painting_mode: BorderPaintingMode,
background_and_border_level: BackgroundAndBorderLevel,
clip: &ClippingRegion);
@ -631,37 +635,67 @@ impl FragmentDisplayListBuilding for Fragment {
}
}
fn build_display_list_for_borders_if_applicable(&self,
style: &ComputedValues,
display_list: &mut DisplayList,
abs_bounds: &Rect<Au>,
level: StackingLevel,
clip: &ClippingRegion) {
let border = style.logical_border_width();
fn build_display_list_for_borders_if_applicable(
&self,
style: &ComputedValues,
border_painting_mode: BorderPaintingMode,
display_list: &mut DisplayList,
bounds: &Rect<Au>,
level: StackingLevel,
clip: &ClippingRegion) {
let mut border = style.logical_border_width();
match border_painting_mode {
BorderPaintingMode::Separate => {}
BorderPaintingMode::Collapse(collapsed_borders) => {
collapsed_borders.adjust_border_widths_for_painting(&mut border)
}
BorderPaintingMode::Hidden => return,
}
if border.is_zero() {
return
}
let top_color = style.resolve_color(style.get_border().border_top_color);
let right_color = style.resolve_color(style.get_border().border_right_color);
let bottom_color = style.resolve_color(style.get_border().border_bottom_color);
let left_color = style.resolve_color(style.get_border().border_left_color);
let border_style_struct = style.get_border();
let mut colors = SideOffsets2D::new(border_style_struct.border_top_color,
border_style_struct.border_right_color,
border_style_struct.border_bottom_color,
border_style_struct.border_left_color);
let mut border_style = SideOffsets2D::new(border_style_struct.border_top_style,
border_style_struct.border_right_style,
border_style_struct.border_bottom_style,
border_style_struct.border_left_style);
if let BorderPaintingMode::Collapse(collapsed_borders) = border_painting_mode {
collapsed_borders.adjust_border_colors_and_styles_for_painting(&mut colors,
&mut border_style,
style.writing_mode);
}
let colors = SideOffsets2D::new(style.resolve_color(colors.top),
style.resolve_color(colors.right),
style.resolve_color(colors.bottom),
style.resolve_color(colors.left));
// If this border collapses, then we draw outside the boundaries we were given.
let mut bounds = *bounds;
if let BorderPaintingMode::Collapse(collapsed_borders) = border_painting_mode {
collapsed_borders.adjust_border_bounds_for_painting(&mut bounds, style.writing_mode)
}
// Append the border to the display list.
display_list.push(DisplayItem::BorderClass(box BorderDisplayItem {
base: BaseDisplayItem::new(*abs_bounds,
DisplayItemMetadata::new(self.node, style, Cursor::DefaultCursor),
base: BaseDisplayItem::new(bounds,
DisplayItemMetadata::new(self.node,
style,
Cursor::DefaultCursor),
(*clip).clone()),
border_widths: border.to_physical(style.writing_mode),
color: SideOffsets2D::new(top_color.to_gfx_color(),
right_color.to_gfx_color(),
bottom_color.to_gfx_color(),
left_color.to_gfx_color()),
style: SideOffsets2D::new(style.get_border().border_top_style,
style.get_border().border_right_style,
style.get_border().border_bottom_style,
style.get_border().border_left_style),
radius: build_border_radius(abs_bounds, style.get_border()),
color: SideOffsets2D::new(colors.top.to_gfx_color(),
colors.right.to_gfx_color(),
colors.bottom.to_gfx_color(),
colors.left.to_gfx_color()),
style: border_style,
radius: build_border_radius(&bounds, border_style_struct),
}), level);
}
@ -693,7 +727,9 @@ impl FragmentDisplayListBuilding for Fragment {
let color = style.resolve_color(style.get_outline().outline_color).to_gfx_color();
display_list.outlines.push_back(DisplayItem::BorderClass(box BorderDisplayItem {
base: BaseDisplayItem::new(bounds,
DisplayItemMetadata::new(self.node, style, Cursor::DefaultCursor),
DisplayItemMetadata::new(self.node,
style,
Cursor::DefaultCursor),
(*clip).clone()),
border_widths: SideOffsets2D::new_all_same(width),
color: SideOffsets2D::new_all_same(color),
@ -715,7 +751,9 @@ impl FragmentDisplayListBuilding for Fragment {
// Compute the text fragment bounds and draw a border surrounding them.
display_list.content.push_back(DisplayItem::BorderClass(box BorderDisplayItem {
base: BaseDisplayItem::new(*stacking_relative_border_box,
DisplayItemMetadata::new(self.node, style, Cursor::DefaultCursor),
DisplayItemMetadata::new(self.node,
style,
Cursor::DefaultCursor),
(*clip).clone()),
border_widths: SideOffsets2D::new_all_same(Au::from_px(1)),
color: SideOffsets2D::new_all_same(color::rgb(0, 0, 200)),
@ -785,6 +823,7 @@ impl FragmentDisplayListBuilding for Fragment {
stacking_relative_flow_origin: &Point2D<Au>,
relative_containing_block_size: &LogicalSize<Au>,
relative_containing_block_mode: WritingMode,
border_painting_mode: BorderPaintingMode,
background_and_border_level: BackgroundAndBorderLevel,
clip: &ClippingRegion) {
if self.style().get_inheritedbox().visibility != visibility::T::visible {
@ -845,6 +884,7 @@ impl FragmentDisplayListBuilding for Fragment {
&clip);
self.build_display_list_for_borders_if_applicable(
&**style,
border_painting_mode,
display_list,
&stacking_relative_border_box,
level,
@ -870,6 +910,7 @@ impl FragmentDisplayListBuilding for Fragment {
&stacking_relative_border_box,
&clip);
self.build_display_list_for_borders_if_applicable(&*self.style,
border_painting_mode,
display_list,
&stacking_relative_border_box,
level,
@ -1262,26 +1303,33 @@ pub trait BlockFlowDisplayListBuilding {
fn build_display_list_for_block_base(&mut self,
display_list: &mut DisplayList,
layout_context: &LayoutContext,
border_painting_mode: BorderPaintingMode,
background_border_level: BackgroundAndBorderLevel);
fn build_display_list_for_static_block(&mut self,
display_list: Box<DisplayList>,
layout_context: &LayoutContext,
border_painting_mode: BorderPaintingMode,
background_border_level: BackgroundAndBorderLevel);
fn build_display_list_for_absolutely_positioned_block(&mut self,
display_list: Box<DisplayList>,
layout_context: &LayoutContext);
fn build_display_list_for_absolutely_positioned_block(
&mut self,
display_list: Box<DisplayList>,
layout_context: &LayoutContext,
border_painting_mode: BorderPaintingMode);
fn build_display_list_for_floating_block(&mut self,
display_list: Box<DisplayList>,
layout_context: &LayoutContext);
layout_context: &LayoutContext,
border_painting_mode: BorderPaintingMode);
fn build_display_list_for_block(&mut self,
display_list: Box<DisplayList>,
layout_context: &LayoutContext);
layout_context: &LayoutContext,
border_painting_mode: BorderPaintingMode);
}
impl BlockFlowDisplayListBuilding for BlockFlow {
fn build_display_list_for_block_base(&mut self,
display_list: &mut DisplayList,
layout_context: &LayoutContext,
border_painting_mode: BorderPaintingMode,
background_border_level: BackgroundAndBorderLevel) {
// Add the box that starts the block context.
let clip = if self.fragment.establishes_stacking_context() {
@ -1289,17 +1337,15 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
} else {
self.base.clip.clone()
};
self.fragment.build_display_list(display_list,
layout_context,
&self.base.stacking_relative_position,
&self.base
.absolute_position_info
.relative_containing_block_size,
self.base
.absolute_position_info
.relative_containing_block_mode,
background_border_level,
&clip);
self.fragment
.build_display_list(display_list,
layout_context,
&self.base.stacking_relative_position,
&self.base.absolute_position_info.relative_containing_block_size,
self.base.absolute_position_info.relative_containing_block_mode,
border_painting_mode,
background_border_level,
&clip);
// Add children.
for kid in self.base.children.iter_mut() {
@ -1312,9 +1358,11 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
fn build_display_list_for_static_block(&mut self,
mut display_list: Box<DisplayList>,
layout_context: &LayoutContext,
border_painting_mode: BorderPaintingMode,
background_border_level: BackgroundAndBorderLevel) {
self.build_display_list_for_block_base(&mut *display_list,
layout_context,
border_painting_mode,
background_border_level);
self.base.display_list_building_result = if self.fragment.establishes_stacking_context() {
@ -1324,11 +1372,14 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
}
}
fn build_display_list_for_absolutely_positioned_block(&mut self,
mut display_list: Box<DisplayList>,
layout_context: &LayoutContext) {
fn build_display_list_for_absolutely_positioned_block(
&mut self,
mut display_list: Box<DisplayList>,
layout_context: &LayoutContext,
border_painting_mode: BorderPaintingMode) {
self.build_display_list_for_block_base(&mut *display_list,
layout_context,
border_painting_mode,
BackgroundAndBorderLevel::RootOfStackingContext);
if !self.base.absolute_position_info.layers_needed_for_positioned_flows &&
@ -1359,9 +1410,11 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
fn build_display_list_for_floating_block(&mut self,
mut display_list: Box<DisplayList>,
layout_context: &LayoutContext) {
layout_context: &LayoutContext,
border_painting_mode: BorderPaintingMode) {
self.build_display_list_for_block_base(&mut *display_list,
layout_context,
border_painting_mode,
BackgroundAndBorderLevel::RootOfStackingContext);
display_list.form_float_pseudo_stacking_context();
@ -1375,16 +1428,22 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
fn build_display_list_for_block(&mut self,
display_list: Box<DisplayList>,
layout_context: &LayoutContext) {
layout_context: &LayoutContext,
border_painting_mode: BorderPaintingMode) {
if self.base.flags.is_float() {
// TODO(#2009, pcwalton): This is a pseudo-stacking context. We need to merge `z-index:
// auto` kids into the parent stacking context, when that is supported.
self.build_display_list_for_floating_block(display_list, layout_context);
self.build_display_list_for_floating_block(display_list,
layout_context,
border_painting_mode);
} else if self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) {
self.build_display_list_for_absolutely_positioned_block(display_list, layout_context);
self.build_display_list_for_absolutely_positioned_block(display_list,
layout_context,
border_painting_mode);
} else {
self.build_display_list_for_static_block(display_list,
layout_context,
border_painting_mode,
BackgroundAndBorderLevel::Block);
}
}
@ -1412,6 +1471,7 @@ impl InlineFlowDisplayListBuilding for InlineFlow {
self.base
.absolute_position_info
.relative_containing_block_mode,
BorderPaintingMode::Separate,
BackgroundAndBorderLevel::Content,
&self.base.clip);
@ -1474,12 +1534,15 @@ impl ListItemFlowDisplayListBuilding for ListItemFlow {
.base
.absolute_position_info
.relative_containing_block_mode,
BorderPaintingMode::Separate,
BackgroundAndBorderLevel::Content,
&self.block_flow.base.clip);
}
// Draw the rest of the block.
self.block_flow.build_display_list_for_block(display_list, layout_context)
self.block_flow.build_display_list_for_block(display_list,
layout_context,
BorderPaintingMode::Separate)
}
}
@ -1539,7 +1602,9 @@ fn fmin(a: f32, b: f32) -> f32 {
fn position_to_offset(position: LengthOrPercentage, Au(total_length): Au) -> f32 {
match position {
LengthOrPercentage::Length(Au(length)) => fmin(1.0, (length as f32) / (total_length as f32)),
LengthOrPercentage::Length(Au(length)) => {
fmin(1.0, (length as f32) / (total_length as f32))
}
LengthOrPercentage::Percentage(percentage) => percentage as f32,
}
}
@ -1610,3 +1675,15 @@ impl ToGfxColor for RGBA {
color::rgba(self.red, self.green, self.blue, self.alpha)
}
}
/// Describes how to paint the borders.
#[derive(Copy, Clone)]
pub enum BorderPaintingMode<'a> {
/// Paint borders separately (`border-collapse: separate`).
Separate,
/// Paint collapsed borders.
Collapse(&'a CollapsedBordersForCell),
/// Paint no borders.
Hidden,
}

View file

@ -48,14 +48,12 @@ use wrapper::ThreadSafeLayoutNode;
use geom::{Point2D, Rect, Size2D};
use gfx::display_list::ClippingRegion;
use rustc_serialize::{Encoder, Encodable};
use msg::constellation_msg::ConstellationChan;
use msg::compositor_msg::LayerId;
use util::geometry::{Au, ZERO_RECT};
use util::logical_geometry::{LogicalRect, LogicalSize, WritingMode};
use std::mem;
use msg::constellation_msg::ConstellationChan;
use rustc_serialize::{Encoder, Encodable};
use std::fmt;
use std::iter::Zip;
use std::mem;
use std::num::FromPrimitive;
use std::raw;
use std::slice::IterMut;
@ -64,6 +62,8 @@ use std::sync::atomic::{AtomicUsize, Ordering};
use style::computed_values::{clear, empty_cells, float, position, text_align};
use style::properties::ComputedValues;
use style::values::computed::LengthOrPercentageOrAuto;
use util::geometry::{Au, ZERO_RECT};
use util::logical_geometry::{LogicalRect, LogicalSize, WritingMode};
/// Virtual methods that make up a float context.
///
@ -358,7 +358,7 @@ pub fn child_iter<'a>(flow: &'a mut Flow) -> MutFlowListIterator<'a> {
pub trait ImmutableFlowUtils {
// Convenience functions
/// Returns true if this flow is a block or a float flow.
/// Returns true if this flow is a block flow or subclass thereof.
fn is_block_like(self) -> bool;
/// Returns true if this flow is a table flow.

View file

@ -40,8 +40,8 @@ use std::sync::mpsc::Sender;
use std::sync::{Arc, Mutex};
use string_cache::Atom;
use style::computed_values::content::ContentItem;
use style::computed_values::{clear, mix_blend_mode, overflow_wrap, position, text_align};
use style::computed_values::{text_decoration, white_space, word_break};
use style::computed_values::{border_collapse, clear, mix_blend_mode, overflow_wrap, position};
use style::computed_values::{text_align, text_decoration, white_space, word_break};
use style::node::{TElement, TNode};
use style::properties::{ComputedValues, cascade_anonymous, make_border};
use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};
@ -93,6 +93,7 @@ pub struct Fragment {
/// border, but not margin.
///
/// NB: This does not account for relative positioning.
/// NB: Collapsed borders are not included in this.
pub border_box: LogicalRect<Au>,
/// The sum of border and padding; i.e. the distance from the edge of the border box to the
@ -387,8 +388,16 @@ impl ReplacedImageFragmentInfo {
for_node: untrusted_node,
computed_inline_size: None,
computed_block_size: None,
dom_inline_size: if is_vertical { dom_height } else { dom_width },
dom_block_size: if is_vertical { dom_width } else { dom_height },
dom_inline_size: if is_vertical {
dom_height
} else {
dom_width
},
dom_block_size: if is_vertical {
dom_width
} else {
dom_height
},
writing_mode_is_vertical: is_vertical,
}
}
@ -410,16 +419,10 @@ impl ReplacedImageFragmentInfo {
pub fn style_length(style_length: LengthOrPercentageOrAuto,
dom_length: Option<Au>,
container_inline_size: Au) -> MaybeAuto {
match (MaybeAuto::from_style(style_length,container_inline_size),dom_length) {
(MaybeAuto::Specified(length),_) => {
MaybeAuto::Specified(length)
},
(MaybeAuto::Auto,Some(length)) => {
MaybeAuto::Specified(length)
},
(MaybeAuto::Auto,None) => {
MaybeAuto::Auto
}
match (MaybeAuto::from_style(style_length,container_inline_size), dom_length) {
(MaybeAuto::Specified(length), _) => MaybeAuto::Specified(length),
(MaybeAuto::Auto, Some(length)) => MaybeAuto::Specified(length),
(MaybeAuto::Auto, None) => MaybeAuto::Auto,
}
}
@ -520,8 +523,8 @@ impl ReplacedImageFragmentInfo {
}
}
/// A fragment that represents an inline frame (iframe). This stores the pipeline ID so that the size
/// of this iframe can be communicated via the constellation to the iframe's own layout task.
/// A fragment that represents an inline frame (iframe). This stores the pipeline ID so that the
/// size of this iframe can be communicated via the constellation to the iframe's own layout task.
#[derive(Clone)]
pub struct IframeFragmentInfo {
/// The pipeline ID of this iframe.
@ -878,20 +881,34 @@ impl Fragment {
SpecificFragmentInfo::InlineBlock(_) => {
QuantitiesIncludedInIntrinsicInlineSizes::all()
}
SpecificFragmentInfo::Table |
SpecificFragmentInfo::TableCell => {
INTRINSIC_INLINE_SIZE_INCLUDES_PADDING |
INTRINSIC_INLINE_SIZE_INCLUDES_BORDER |
INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED
SpecificFragmentInfo::Table | SpecificFragmentInfo::TableCell => {
let base_quantities = INTRINSIC_INLINE_SIZE_INCLUDES_PADDING |
INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED;
if self.style.get_inheritedtable().border_collapse ==
border_collapse::T::separate {
base_quantities | INTRINSIC_INLINE_SIZE_INCLUDES_BORDER
} else {
base_quantities
}
}
SpecificFragmentInfo::TableWrapper => {
INTRINSIC_INLINE_SIZE_INCLUDES_MARGINS |
INTRINSIC_INLINE_SIZE_INCLUDES_BORDER |
INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED
let base_quantities = INTRINSIC_INLINE_SIZE_INCLUDES_MARGINS |
INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED;
if self.style.get_inheritedtable().border_collapse ==
border_collapse::T::separate {
base_quantities | INTRINSIC_INLINE_SIZE_INCLUDES_BORDER
} else {
base_quantities
}
}
SpecificFragmentInfo::TableRow => {
INTRINSIC_INLINE_SIZE_INCLUDES_BORDER |
INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED
let base_quantities = INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED;
if self.style.get_inheritedtable().border_collapse ==
border_collapse::T::separate {
base_quantities | INTRINSIC_INLINE_SIZE_INCLUDES_BORDER
} else {
base_quantities
}
}
SpecificFragmentInfo::ScannedText(_) |
SpecificFragmentInfo::TableColumn(_) |
@ -946,12 +963,13 @@ impl Fragment {
fn style_specified_intrinsic_inline_size(&self) -> IntrinsicISizesContribution {
let flags = self.quantities_included_in_intrinsic_inline_size();
let style = self.style();
let (min_inline_size, specified) = if flags.contains(INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED) {
(model::specified(style.min_inline_size(), Au(0)),
MaybeAuto::from_style(style.content_inline_size(), Au(0)).specified_or_zero())
} else {
(Au(0), Au(0))
};
let (min_inline_size, specified) =
if flags.contains(INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED) {
(model::specified(style.min_inline_size(), Au(0)),
MaybeAuto::from_style(style.content_inline_size(), Au(0)).specified_or_zero())
} else {
(Au(0), Au(0))
};
// FIXME(#2261, pcwalton): This won't work well for inlines: is this OK?
let surrounding_inline_size = self.surrounding_intrinsic_inline_size();
@ -974,7 +992,7 @@ impl Fragment {
/// Returns the sum of the inline-sizes of all the borders of this fragment. Note that this
/// can be expensive to compute, so if possible use the `border_padding` field instead.
#[inline]
pub fn border_width(&self) -> LogicalMargin<Au> {
fn border_width(&self) -> LogicalMargin<Au> {
let style_border_width = match self.specific {
SpecificFragmentInfo::ScannedText(_) => LogicalMargin::zero(self.style.writing_mode),
_ => self.style().logical_border_width(),
@ -983,8 +1001,10 @@ impl Fragment {
match self.inline_context {
None => style_border_width,
Some(ref inline_fragment_context) => {
inline_fragment_context.styles.iter().fold(style_border_width,
|acc, style| acc + style.logical_border_width())
inline_fragment_context.styles
.iter()
.fold(style_border_width,
|acc, style| acc + style.logical_border_width())
}
}
}
@ -996,7 +1016,10 @@ impl Fragment {
/// (for example, via constraint solving for blocks).
pub fn compute_inline_direction_margins(&mut self, containing_block_inline_size: Au) {
match self.specific {
SpecificFragmentInfo::Table | SpecificFragmentInfo::TableCell | SpecificFragmentInfo::TableRow | SpecificFragmentInfo::TableColumn(_) => {
SpecificFragmentInfo::Table |
SpecificFragmentInfo::TableCell |
SpecificFragmentInfo::TableRow |
SpecificFragmentInfo::TableColumn(_) => {
self.margin.inline_start = Au(0);
self.margin.inline_end = Au(0)
}
@ -1019,7 +1042,10 @@ impl Fragment {
/// (for example, via constraint solving for absolutely-positioned flows).
pub fn compute_block_direction_margins(&mut self, containing_block_inline_size: Au) {
match self.specific {
SpecificFragmentInfo::Table | SpecificFragmentInfo::TableCell | SpecificFragmentInfo::TableRow | SpecificFragmentInfo::TableColumn(_) => {
SpecificFragmentInfo::Table |
SpecificFragmentInfo::TableCell |
SpecificFragmentInfo::TableRow |
SpecificFragmentInfo::TableColumn(_) => {
self.margin.block_start = Au(0);
self.margin.block_end = Au(0)
}
@ -1040,9 +1066,17 @@ impl Fragment {
/// Computes the border and padding in both inline and block directions from the containing
/// block inline-size and the style. After this call, the `border_padding` field will be
/// correct.
pub fn compute_border_and_padding(&mut self, containing_block_inline_size: Au) {
///
/// TODO(pcwalton): Remove `border_collapse`; we can figure it out from our style and specific
/// fragment info.
pub fn compute_border_and_padding(&mut self,
containing_block_inline_size: Au,
border_collapse: border_collapse::T) {
// Compute border.
let border = self.border_width();
let border = match border_collapse {
border_collapse::T::separate => self.border_width(),
border_collapse::T::collapse => LogicalMargin::zero(self.style.writing_mode),
};
// Compute padding.
let padding = match self.specific {
@ -1050,21 +1084,27 @@ impl Fragment {
SpecificFragmentInfo::TableWrapper => LogicalMargin::zero(self.style.writing_mode),
_ => {
let style_padding = match self.specific {
SpecificFragmentInfo::ScannedText(_) => LogicalMargin::zero(self.style.writing_mode),
SpecificFragmentInfo::ScannedText(_) => {
LogicalMargin::zero(self.style.writing_mode)
}
_ => model::padding_from_style(self.style(), containing_block_inline_size),
};
match self.inline_context {
None => style_padding,
Some(ref inline_fragment_context) => {
inline_fragment_context.styles.iter().fold(style_padding,
|acc, style| acc + model::padding_from_style(&**style, Au(0)))
inline_fragment_context.styles
.iter()
.fold(style_padding, |acc, style| {
acc + model::padding_from_style(&**style,
Au(0))
})
}
}
}
};
self.border_padding = border + padding;
self.border_padding = border + padding
}
// Return offset from original position because of `position: relative`.
@ -1073,14 +1113,18 @@ impl Fragment {
-> LogicalSize<Au> {
let offsets = style.logical_position();
let offset_i = if offsets.inline_start != LengthOrPercentageOrAuto::Auto {
MaybeAuto::from_style(offsets.inline_start, container_size.inline).specified_or_zero()
MaybeAuto::from_style(offsets.inline_start,
container_size.inline).specified_or_zero()
} else {
-MaybeAuto::from_style(offsets.inline_end, container_size.inline).specified_or_zero()
-MaybeAuto::from_style(offsets.inline_end,
container_size.inline).specified_or_zero()
};
let offset_b = if offsets.block_start != LengthOrPercentageOrAuto::Auto {
MaybeAuto::from_style(offsets.block_start, container_size.inline).specified_or_zero()
MaybeAuto::from_style(offsets.block_start,
container_size.inline).specified_or_zero()
} else {
-MaybeAuto::from_style(offsets.block_end, container_size.inline).specified_or_zero()
-MaybeAuto::from_style(offsets.block_end,
container_size.inline).specified_or_zero()
};
LogicalSize::new(style.writing_mode, offset_i, offset_b)
}
@ -1254,7 +1298,7 @@ impl Fragment {
/// TODO: What exactly does this function return? Why is it Au(0) for
/// SpecificFragmentInfo::Generic?
/// `SpecificFragmentInfo::Generic`?
pub fn content_inline_size(&self) -> Au {
match self.specific {
SpecificFragmentInfo::Generic |

View file

@ -15,7 +15,7 @@ use layout_debug;
use model::IntrinsicISizesContribution;
use text;
use collections::{VecDeque};
use collections::VecDeque;
use geom::{Point2D, Rect};
use gfx::font::FontMetrics;
use gfx::font_context::FontContext;
@ -28,8 +28,8 @@ use std::num::ToPrimitive;
use std::ops::{Add, Sub, Mul, Div, Rem, Neg, Shl, Shr, Not, BitOr, BitAnd, BitXor};
use std::sync::Arc;
use std::u16;
use style::computed_values::{display, overflow_x, text_align, text_justify, text_overflow};
use style::computed_values::{vertical_align, white_space};
use style::computed_values::{border_collapse, display, overflow_x, text_align, text_justify};
use style::computed_values::{text_overflow, vertical_align, white_space};
use style::properties::ComputedValues;
use util::geometry::{Au, MAX_AU, ZERO_RECT};
use util::logical_geometry::{LogicalRect, LogicalSize, WritingMode};
@ -1197,7 +1197,7 @@ impl Flow for InlineFlow {
{
let this = &mut *self;
for fragment in this.fragments.fragments.iter_mut() {
fragment.compute_border_and_padding(inline_size);
fragment.compute_border_and_padding(inline_size, border_collapse::T::separate);
fragment.compute_block_direction_margins(inline_size);
fragment.compute_inline_direction_margins(inline_size);
fragment.assign_replaced_inline_size_if_necessary(inline_size);

View file

@ -9,6 +9,7 @@
use block::{self, BlockFlow, CandidateBSizeIterator, ISizeAndMarginsComputer};
use block::{ISizeConstraintInput, ISizeConstraintSolution};
use context::LayoutContext;
use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode};
use floats::FloatKind;
use flow::{self, Flow, FlowClass, IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS};
use flow::{ImmutableFlowUtils};
@ -16,20 +17,23 @@ use fragment::{Fragment, FragmentBorderBoxIterator};
use incremental::{REFLOW, REFLOW_OUT_OF_FLOW};
use layout_debug;
use model::{IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto};
use table_row::CellIntrinsicInlineSize;
use table_row::{self, CellIntrinsicInlineSize, CollapsedBorder, CollapsedBorderProvenance};
use table_row::{TableRowFlow};
use table_wrapper::TableLayout;
use wrapper::ThreadSafeLayoutNode;
use geom::{Point2D, Rect};
use std::cmp::max;
use gfx::display_list::DisplayList;
use std::cmp;
use std::fmt;
use std::iter;
use std::sync::Arc;
use style::computed_values::{border_collapse, border_spacing, table_layout};
use style::properties::ComputedValues;
use style::values::CSSFloat;
use style::values::computed::LengthOrPercentageOrAuto;
use util::geometry::Au;
use util::logical_geometry::{LogicalRect, WritingMode};
use util::logical_geometry::LogicalRect;
/// A table flow corresponded to the table's internal table fragment under a table wrapper flow.
/// The properties `position`, `float`, and `margin-*` are used on the table wrapper fragment,
@ -42,10 +46,18 @@ pub struct TableFlow {
/// intrinsic inline-size bubbling.
pub column_intrinsic_inline_sizes: Vec<ColumnIntrinsicInlineSize>,
/// Information about the actual inline-sizes of each column, computed top-down during actual
/// Information about the actual inline sizes of each column, computed top-down during actual
/// inline-size bubbling.
pub column_computed_inline_sizes: Vec<ColumnComputedInlineSize>,
/// The final width of the borders in the inline direction for each cell, computed by the
/// entire table and pushed down into each row during inline size computation.
pub collapsed_inline_direction_border_widths_for_table: Vec<Au>,
/// The final width of the borders in the block direction for each cell, computed by the
/// entire table and pushed down into each row during inline size computation.
pub collapsed_block_direction_border_widths_for_table: Vec<Au>,
/// Table-layout property
pub table_layout: TableLayout,
}
@ -65,6 +77,8 @@ impl TableFlow {
block_flow: block_flow,
column_intrinsic_inline_sizes: Vec::new(),
column_computed_inline_sizes: Vec::new(),
collapsed_inline_direction_border_widths_for_table: Vec::new(),
collapsed_block_direction_border_widths_for_table: Vec::new(),
table_layout: table_layout
}
}
@ -84,6 +98,8 @@ impl TableFlow {
block_flow: block_flow,
column_intrinsic_inline_sizes: Vec::new(),
column_computed_inline_sizes: Vec::new(),
collapsed_inline_direction_border_widths_for_table: Vec::new(),
collapsed_block_direction_border_widths_for_table: Vec::new(),
table_layout: table_layout
}
}
@ -109,10 +125,10 @@ impl TableFlow {
} else {
let column_size = &child_cell_inline_size.column_size;
*parent_sizes = ColumnIntrinsicInlineSize {
minimum_length: max(parent_sizes.minimum_length,
column_size.minimum_length),
minimum_length: cmp::max(parent_sizes.minimum_length,
column_size.minimum_length),
percentage: parent_sizes.greatest_percentage(column_size),
preferred: max(parent_sizes.preferred, column_size.preferred),
preferred: cmp::max(parent_sizes.preferred, column_size.preferred),
constrained: parent_sizes.constrained || column_size.constrained,
}
}
@ -146,7 +162,7 @@ impl TableFlow {
fn update_column_inline_sizes_for_row(child: &mut Flow,
column_inline_sizes: &mut Vec<ColumnIntrinsicInlineSize>,
computation: &mut IntrinsicISizesContribution,
did_first_row: &mut bool,
first_row: bool,
table_layout: TableLayout) {
// Read column inline-sizes from the table-row, and assign inline-size=0 for the columns
// not defined in the column group.
@ -159,8 +175,7 @@ impl TableFlow {
// Fixed table layout only looks at the first row.
//
// FIXME(pcwalton): This is really inefficient. We should stop after the first row!
if !*did_first_row {
*did_first_row = true;
if first_row {
for cell_inline_size in row.cell_intrinsic_inline_sizes.iter() {
column_inline_sizes.push(cell_inline_size.column_size);
}
@ -175,7 +190,7 @@ impl TableFlow {
}
/// Returns the effective spacing per cell, taking the value of `border-collapse` into account.
fn spacing(&self) -> border_spacing::T {
pub fn spacing(&self) -> border_spacing::T {
let style = self.block_flow.fragment.style();
match style.get_inheritedtable().border_collapse {
border_collapse::T::separate => style.get_inheritedtable().border_spacing,
@ -206,6 +221,10 @@ impl Flow for TableFlow {
&mut self.block_flow
}
fn as_immutable_block(&self) -> &BlockFlow {
&self.block_flow
}
fn column_intrinsic_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<ColumnIntrinsicInlineSize> {
&mut self.column_intrinsic_inline_sizes
}
@ -224,50 +243,165 @@ impl Flow for TableFlow {
// Don't use `compute_intrinsic_inline_sizes` here because that will count padding as
// part of the table, which we don't want to do—it belongs to the table wrapper instead.
self.collapsed_inline_direction_border_widths_for_table = Vec::new();
self.collapsed_block_direction_border_widths_for_table = vec![Au(0)];
let collapsing_borders = self.block_flow
.fragment
.style
.get_inheritedtable()
.border_collapse == border_collapse::T::collapse;
let table_inline_collapsed_borders = if collapsing_borders {
Some(TableInlineCollapsedBorders {
start: CollapsedBorder::inline_start(&*self.block_flow.fragment.style,
CollapsedBorderProvenance::FromTable),
end: CollapsedBorder::inline_end(&*self.block_flow.fragment.style,
CollapsedBorderProvenance::FromTable),
})
} else {
None
};
let mut computation = IntrinsicISizesContribution::new();
let mut did_first_row = false;
for kid in self.block_flow.base.child_iter() {
debug_assert!(kid.is_proper_table_child());
if kid.is_table_colgroup() {
for specified_inline_size in kid.as_table_colgroup().inline_sizes.iter() {
self.column_intrinsic_inline_sizes.push(ColumnIntrinsicInlineSize {
minimum_length: match *specified_inline_size {
LengthOrPercentageOrAuto::Auto | LengthOrPercentageOrAuto::Percentage(_) => Au(0),
LengthOrPercentageOrAuto::Length(length) => length,
},
percentage: match *specified_inline_size {
LengthOrPercentageOrAuto::Auto | LengthOrPercentageOrAuto::Length(_) => 0.0,
LengthOrPercentageOrAuto::Percentage(percentage) => percentage,
},
preferred: Au(0),
constrained: false,
})
}
} else if kid.is_table_rowgroup() {
for grandkid in flow::mut_base(kid).child_iter() {
let mut previous_collapsed_block_end_borders = if collapsing_borders {
PreviousBlockCollapsedBorders::FromTable(CollapsedBorder::block_start(
&*self.block_flow.fragment.style,
CollapsedBorderProvenance::FromTable))
} else {
PreviousBlockCollapsedBorders::NotCollapsingBorders
};
let mut first_row = true;
{
let mut iterator = self.block_flow.base.child_iter().peekable();
while let Some(kid) = iterator.next() {
let next_index_and_sibling = iterator.peek();
let next_collapsed_borders_in_block_direction = if collapsing_borders {
match next_index_and_sibling {
Some(next_sibling) => {
if next_sibling.is_table_rowgroup() {
NextBlockCollapsedBorders::FromNextRow(
&next_sibling.as_immutable_table_rowgroup()
.preliminary_collapsed_borders
.block_start
.as_slice())
} else {
NextBlockCollapsedBorders::FromNextRow(
&next_sibling.as_immutable_table_row()
.preliminary_collapsed_borders
.block_start
.as_slice())
}
}
None => {
NextBlockCollapsedBorders::FromTable(
CollapsedBorder::block_end(&*self.block_flow.fragment.style,
CollapsedBorderProvenance::FromTable))
}
}
} else {
NextBlockCollapsedBorders::NotCollapsingBorders
};
if kid.is_table_colgroup() {
for specified_inline_size in kid.as_table_colgroup().inline_sizes.iter() {
self.column_intrinsic_inline_sizes.push(ColumnIntrinsicInlineSize {
minimum_length: match *specified_inline_size {
LengthOrPercentageOrAuto::Auto |
LengthOrPercentageOrAuto::Percentage(_) => Au(0),
LengthOrPercentageOrAuto::Length(length) => length,
},
percentage: match *specified_inline_size {
LengthOrPercentageOrAuto::Auto |
LengthOrPercentageOrAuto::Length(_) => 0.0,
LengthOrPercentageOrAuto::Percentage(percentage) => percentage,
},
preferred: Au(0),
constrained: false,
})
}
} else if kid.is_table_row() {
TableFlow::update_column_inline_sizes_for_row(
grandkid,
&mut self.column_intrinsic_inline_sizes,
&mut computation,
&mut did_first_row,
self.table_layout)
kid,
&mut self.column_intrinsic_inline_sizes,
&mut computation,
first_row,
self.table_layout);
if collapsing_borders {
perform_border_collapse_for_row(
kid.as_table_row(),
table_inline_collapsed_borders.as_ref().unwrap(),
previous_collapsed_block_end_borders,
next_collapsed_borders_in_block_direction,
&mut self.collapsed_inline_direction_border_widths_for_table,
&mut self.collapsed_block_direction_border_widths_for_table);
previous_collapsed_block_end_borders =
PreviousBlockCollapsedBorders::FromPreviousRow(
kid.as_table_row().final_collapsed_borders.block_end.clone())
}
first_row = false
} else if kid.is_table_rowgroup() {
let mut iterator = flow::mut_base(kid).child_iter().peekable();
while let Some(grandkid) = iterator.next() {
let grandkid_next_sibling = iterator.peek();
let next_collapsed_borders_in_block_direction = if collapsing_borders {
match grandkid_next_sibling {
Some(grandkid_next_sibling) => {
if grandkid_next_sibling.is_table_rowgroup() {
NextBlockCollapsedBorders::FromNextRow(
&grandkid_next_sibling.as_immutable_table_rowgroup()
.preliminary_collapsed_borders
.block_start
.as_slice())
} else {
NextBlockCollapsedBorders::FromNextRow(
&grandkid_next_sibling.as_immutable_table_row()
.preliminary_collapsed_borders
.block_start
.as_slice())
}
}
None => {
NextBlockCollapsedBorders::FromTable(
CollapsedBorder::block_end(
&*self.block_flow.fragment.style,
CollapsedBorderProvenance::FromTable))
}
}
} else {
NextBlockCollapsedBorders::NotCollapsingBorders
};
TableFlow::update_column_inline_sizes_for_row(
grandkid,
&mut self.column_intrinsic_inline_sizes,
&mut computation,
first_row,
self.table_layout);
if collapsing_borders {
perform_border_collapse_for_row(
grandkid.as_table_row(),
table_inline_collapsed_borders.as_ref().unwrap(),
previous_collapsed_block_end_borders,
next_collapsed_borders_in_block_direction,
&mut self.collapsed_inline_direction_border_widths_for_table,
&mut self.collapsed_block_direction_border_widths_for_table);
previous_collapsed_block_end_borders =
PreviousBlockCollapsedBorders::FromPreviousRow(
grandkid.as_table_row()
.final_collapsed_borders
.block_end
.clone())
}
first_row = false
}
}
} else if kid.is_table_row() {
TableFlow::update_column_inline_sizes_for_row(
kid,
&mut self.column_intrinsic_inline_sizes,
&mut computation,
&mut did_first_row,
self.table_layout)
}
}
let spacing = self.block_flow
.fragment
.style()
.get_inheritedtable()
.border_spacing
.horizontal * (self.column_intrinsic_inline_sizes.len() as i32 + 1);
let spacing = self.spacing().horizontal *
(self.column_intrinsic_inline_sizes.len() as i32 + 1);
computation.surrounding_size = computation.surrounding_size + spacing;
self.block_flow.base.intrinsic_inline_sizes = computation.finish()
@ -295,9 +429,11 @@ impl Flow for TableFlow {
}
let inline_size_computer = InternalTable;
let border_collapse = self.block_flow.fragment.style.get_inheritedtable().border_collapse;
inline_size_computer.compute_used_inline_size(&mut self.block_flow,
layout_context,
containing_block_inline_size);
containing_block_inline_size,
border_collapse);
let inline_start_content_edge = self.block_flow.fragment.border_padding.inline_start;
let inline_end_content_edge = self.block_flow.fragment.border_padding.inline_end;
@ -347,15 +483,41 @@ impl Flow for TableFlow {
self.block_flow.base.flags.remove(IMPACTED_BY_LEFT_FLOATS);
self.block_flow.base.flags.remove(IMPACTED_BY_RIGHT_FLOATS);
let info = ChildInlineSizeInfo {
column_computed_inline_sizes: &self.column_computed_inline_sizes,
spacing: spacing_per_cell,
};
let column_computed_inline_sizes = self.column_computed_inline_sizes.as_slice();
let collapsed_inline_direction_border_widths_for_table =
self.collapsed_inline_direction_border_widths_for_table.as_slice();
let mut collapsed_block_direction_border_widths_for_table =
self.collapsed_block_direction_border_widths_for_table.iter().peekable();
self.block_flow.propagate_assigned_inline_size_to_children(layout_context,
inline_start_content_edge,
inline_end_content_edge,
content_inline_size,
Some(info));
|child_flow,
child_index,
content_inline_size,
writing_mode,
inline_start_margin_edge| {
table_row::propagate_column_inline_sizes_to_child(
child_flow,
child_index,
content_inline_size,
writing_mode,
column_computed_inline_sizes,
&spacing_per_cell,
&None,
inline_start_margin_edge);
if child_flow.is_table_row() {
let child_table_row = child_flow.as_table_row();
child_table_row.populate_collapsed_border_spacing(
collapsed_inline_direction_border_widths_for_table.as_slice(),
&mut collapsed_block_direction_border_widths_for_table);
} else if child_flow.is_table_rowgroup() {
let child_table_rowgroup = child_flow.as_table_rowgroup();
child_table_rowgroup.populate_collapsed_border_spacing(
collapsed_inline_direction_border_widths_for_table.as_slice(),
&mut collapsed_block_direction_border_widths_for_table);
}
})
}
fn assign_block_size<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
@ -381,7 +543,18 @@ impl Flow for TableFlow {
}
fn build_display_list(&mut self, layout_context: &LayoutContext) {
self.block_flow.build_display_list(layout_context);
let border_painting_mode = match self.block_flow
.fragment
.style
.get_inheritedtable()
.border_collapse {
border_collapse::T::separate => BorderPaintingMode::Separate,
border_collapse::T::collapse => BorderPaintingMode::Hidden,
};
self.block_flow.build_display_list_for_block(box DisplayList::new(),
layout_context,
border_painting_mode);
}
fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
@ -420,11 +593,13 @@ impl ISizeAndMarginsComputer for InternalTable {
/// CSS Section 10.4: Minimum and Maximum inline-sizes
fn compute_used_inline_size(&self,
block: &mut BlockFlow,
ctx: &LayoutContext,
parent_flow_inline_size: Au) {
layout_context: &LayoutContext,
parent_flow_inline_size: Au,
border_collapse: border_collapse::T) {
let input = self.compute_inline_size_constraint_inputs(block,
parent_flow_inline_size,
ctx);
layout_context,
border_collapse);
let solution = self.solve_inline_size_constraints(block, &input);
self.set_inline_size_constraint_solutions(block, solution);
@ -437,87 +612,6 @@ impl ISizeAndMarginsComputer for InternalTable {
}
}
/// Encapsulates functionality shared among all table-like flows: for now, tables and table
/// rowgroups.
pub trait TableLikeFlow {
/// Lays out the rows of a table.
fn assign_block_size_for_table_like_flow<'a>(&mut self,
layout_context: &'a LayoutContext<'a>,
block_direction_spacing: Au);
}
impl TableLikeFlow for BlockFlow {
fn assign_block_size_for_table_like_flow<'a>(&mut self,
_: &'a LayoutContext<'a>,
block_direction_spacing: Au) {
if self.base.restyle_damage.contains(REFLOW) {
// Our current border-box position.
let block_start_border_padding = self.fragment.border_padding.block_start;
let mut current_block_offset = block_start_border_padding;
// At this point, `current_block_offset` is at the content edge of our box. Now iterate
// over children.
let mut layers_needed_for_descendants = false;
for kid in self.base.child_iter() {
// Mark flows for layerization if necessary to handle painting order correctly.
block::propagate_layer_flag_from_child(&mut layers_needed_for_descendants, kid);
// Account for spacing.
if kid.is_table_row() {
current_block_offset = current_block_offset + block_direction_spacing;
}
// At this point, `current_block_offset` is at the border edge of the child.
flow::mut_base(kid).position.start.b = current_block_offset;
// Move past the child's border box. Do not use the `translate_including_floats`
// function here because the child has already translated floats past its border
// box.
let kid_base = flow::mut_base(kid);
current_block_offset = current_block_offset + kid_base.position.size.block;
}
// Compute any explicitly-specified block size.
// Can't use `for` because we assign to `candidate_block_size_iterator.candidate_value`.
let mut block_size = current_block_offset - block_start_border_padding;
let mut candidate_block_size_iterator = CandidateBSizeIterator::new(
&self.fragment,
self.base.block_container_explicit_block_size);
loop {
match candidate_block_size_iterator.next() {
Some(candidate_block_size) => {
candidate_block_size_iterator.candidate_value =
match candidate_block_size {
MaybeAuto::Auto => block_size,
MaybeAuto::Specified(value) => value
}
}
None => break,
}
}
// Adjust `current_block_offset` as necessary to account for the explicitly-specified
// block-size.
block_size = candidate_block_size_iterator.candidate_value;
let delta = block_size - (current_block_offset - block_start_border_padding);
current_block_offset = current_block_offset + delta;
// Take border, padding, and spacing into account.
let block_end_offset = self.fragment.border_padding.block_end +
block_direction_spacing;
current_block_offset = current_block_offset + block_end_offset;
// Now that `current_block_offset` is at the block-end of the border box, compute the
// final border box position.
self.fragment.border_box.size.block = current_block_offset;
self.fragment.border_box.start.b = Au(0);
self.base.position.size.block = current_block_offset;
}
self.base.restyle_damage.remove(REFLOW_OUT_OF_FLOW | REFLOW);
}
}
/// Information about the intrinsic inline sizes of columns within a table.
///
/// During table inline-size bubbling, we might need to store both a percentage constraint and a
@ -556,7 +650,7 @@ impl ColumnIntrinsicInlineSize {
/// Beware that this is generally only correct for fixed table layout. (Compare CSS 2.1 §
/// 17.5.2.1 with the algorithm in INTRINSIC § 4.)
pub fn minimum(&self, containing_block_inline_size: Au) -> Au {
max(self.minimum_length, containing_block_inline_size.scale_by(self.percentage))
cmp::max(self.minimum_length, containing_block_inline_size.scale_by(self.percentage))
}
/// Returns the higher of the two percentages specified in `self` and `other`.
@ -579,63 +673,206 @@ pub struct ColumnComputedInlineSize {
pub size: Au,
}
/// Inline-size information that we need to push down to table children.
pub struct ChildInlineSizeInfo<'a> {
/// The spacing of the table.
pub spacing: border_spacing::T,
/// The computed inline sizes for each column.
pub column_computed_inline_sizes: &'a [ColumnComputedInlineSize],
pub trait VecExt<T> {
fn push_or_set(&mut self, index: usize, value: T);
fn push_or_mutate(&mut self, index: usize, zero: T) -> &mut T;
}
impl<'a> ChildInlineSizeInfo<'a> {
/// Propagates information computed during inline size assignment to a child of a table, and
/// lays out that child in the inline direction.
pub fn propagate_to_child(&self,
kid: &mut Flow,
child_index: usize,
content_inline_size: Au,
writing_mode: WritingMode,
inline_start_margin_edge: &mut Au) {
// If the child is a table or a row, copy computed inline size information from its parent.
//
// FIXME(pcwalton): This seems inefficient. Reference count it instead?
let inline_size;
if kid.is_table() {
let table_kid = kid.as_table();
table_kid.column_computed_inline_sizes = self.column_computed_inline_sizes.to_vec();
inline_size = content_inline_size
} else if kid.is_table_rowgroup() {
let table_rowgroup_kid = kid.as_table_rowgroup();
table_rowgroup_kid.column_computed_inline_sizes =
self.column_computed_inline_sizes.to_vec();
table_rowgroup_kid.spacing = self.spacing;
inline_size = content_inline_size
} else if kid.is_table_row() {
let table_row_kid = kid.as_table_row();
table_row_kid.column_computed_inline_sizes =
self.column_computed_inline_sizes.to_vec();
table_row_kid.spacing = self.spacing;
inline_size = content_inline_size
} else if kid.is_table_cell() {
// Take spacing into account.
*inline_start_margin_edge = *inline_start_margin_edge + self.spacing.horizontal;
inline_size = self.column_computed_inline_sizes[child_index].size;
impl<T> VecExt<T> for Vec<T> {
fn push_or_set(&mut self, index: usize, value: T) {
if index < self.len() {
self[index] = value
} else {
// ISize of kid flow is our content inline-size.
inline_size = content_inline_size
debug_assert!(index == self.len());
self.push(value)
}
}
{
let kid_base = flow::mut_base(kid);
kid_base.position.start.i = *inline_start_margin_edge;
kid_base.block_container_inline_size = inline_size;
kid_base.block_container_writing_mode = writing_mode
}
// Move over for the next table cell.
if kid.is_table_cell() {
*inline_start_margin_edge = *inline_start_margin_edge + inline_size
fn push_or_mutate(&mut self, index: usize, zero: T) -> &mut T {
if index >= self.len() {
debug_assert!(index == self.len());
self.push(zero)
}
&mut self[index]
}
}
/// Updates the border styles in the block direction for a single row. This function should
/// only be called if border collapsing is on. It is factored out into a separate function
/// because we process children of rowgroups too.
fn perform_border_collapse_for_row(child_table_row: &mut TableRowFlow,
table_inline_borders: &TableInlineCollapsedBorders,
previous_block_borders: PreviousBlockCollapsedBorders,
next_block_borders: NextBlockCollapsedBorders,
inline_spacing: &mut Vec<Au>,
block_spacing: &mut Vec<Au>) {
// Compute interior inline borders.
for (i, this_inline_border) in child_table_row.preliminary_collapsed_borders
.inline
.iter()
.enumerate() {
child_table_row.final_collapsed_borders.inline.push_or_set(i, *this_inline_border);
let inline_spacing = inline_spacing.push_or_mutate(i, Au(0));
*inline_spacing = cmp::max(*inline_spacing, this_inline_border.width)
}
// Collapse edge interior borders with the table.
if let Some(ref mut first_inline_borders) = child_table_row.final_collapsed_borders
.inline
.get_mut(0) {
first_inline_borders.combine(&table_inline_borders.start)
}
if let Some(ref mut last_inline_borders) = child_table_row.final_collapsed_borders
.inline
.last_mut() {
last_inline_borders.combine(&table_inline_borders.end)
}
// Compute block-start borders.
match previous_block_borders {
PreviousBlockCollapsedBorders::FromPreviousRow(previous_block_borders) => {
child_table_row.final_collapsed_borders.block_start = previous_block_borders
}
PreviousBlockCollapsedBorders::FromTable(collapsed_border) => {
child_table_row.final_collapsed_borders.block_start =
iter::repeat(collapsed_border).take(child_table_row.block_flow.base.children.len())
.collect()
}
PreviousBlockCollapsedBorders::NotCollapsingBorders => {}
}
// Compute block-end borders.
let next_block = &mut child_table_row.final_collapsed_borders.block_end;
block_spacing.push(Au(0));
let block_spacing = block_spacing.last_mut().unwrap();
for (i, this_block_border) in child_table_row.preliminary_collapsed_borders
.block_end
.iter()
.enumerate() {
let next_block = next_block.push_or_mutate(i, *this_block_border);
match next_block_borders {
NextBlockCollapsedBorders::FromNextRow(next_block_borders) => {
next_block.combine(&next_block_borders[i]);
}
NextBlockCollapsedBorders::FromTable(ref next_block_borders) => {
next_block.combine(next_block_borders);
}
NextBlockCollapsedBorders::NotCollapsingBorders => {}
}
*block_spacing = cmp::max(*block_spacing, next_block.width)
}
}
/// Encapsulates functionality shared among all table-like flows: for now, tables and table
/// rowgroups.
pub trait TableLikeFlow {
/// Lays out the rows of a table.
fn assign_block_size_for_table_like_flow<'a>(&mut self,
layout_context: &'a LayoutContext<'a>,
block_direction_spacing: Au);
}
impl TableLikeFlow for BlockFlow {
fn assign_block_size_for_table_like_flow<'a>(&mut self,
_: &'a LayoutContext<'a>,
block_direction_spacing: Au) {
debug_assert!(self.fragment.style.get_inheritedtable().border_collapse ==
border_collapse::T::separate || block_direction_spacing == Au(0));
if self.base.restyle_damage.contains(REFLOW) {
// Our current border-box position.
let block_start_border_padding = self.fragment.border_padding.block_start;
let mut current_block_offset = block_start_border_padding;
// At this point, `current_block_offset` is at the content edge of our box. Now iterate
// over children.
let mut layers_needed_for_descendants = false;
for kid in self.base.child_iter() {
// Mark flows for layerization if necessary to handle painting order correctly.
block::propagate_layer_flag_from_child(&mut layers_needed_for_descendants, kid);
// Account for spacing or collapsed borders.
if kid.is_table_row() {
let child_table_row = kid.as_table_row();
current_block_offset = current_block_offset +
match self.fragment.style.get_inheritedtable().border_collapse {
border_collapse::T::separate => block_direction_spacing,
border_collapse::T::collapse => {
child_table_row.collapsed_border_spacing.block_start
}
}
}
// At this point, `current_block_offset` is at the border edge of the child.
flow::mut_base(kid).position.start.b = current_block_offset;
// Move past the child's border box. Do not use the `translate_including_floats`
// function here because the child has already translated floats past its border
// box.
let kid_base = flow::mut_base(kid);
current_block_offset = current_block_offset + kid_base.position.size.block;
}
// Compute any explicitly-specified block size.
// Can't use `for` because we assign to
// `candidate_block_size_iterator.candidate_value`.
let mut block_size = current_block_offset - block_start_border_padding;
let mut candidate_block_size_iterator = CandidateBSizeIterator::new(
&self.fragment,
self.base.block_container_explicit_block_size);
loop {
match candidate_block_size_iterator.next() {
Some(candidate_block_size) => {
candidate_block_size_iterator.candidate_value =
match candidate_block_size {
MaybeAuto::Auto => block_size,
MaybeAuto::Specified(value) => value
}
}
None => break,
}
}
// Adjust `current_block_offset` as necessary to account for the explicitly-specified
// block-size.
block_size = candidate_block_size_iterator.candidate_value;
let delta = block_size - (current_block_offset - block_start_border_padding);
current_block_offset = current_block_offset + delta;
// Take border, padding, and spacing into account.
let block_end_offset = self.fragment.border_padding.block_end +
block_direction_spacing;
current_block_offset = current_block_offset + block_end_offset;
// Now that `current_block_offset` is at the block-end of the border box, compute the
// final border box position.
self.fragment.border_box.size.block = current_block_offset;
self.fragment.border_box.start.b = Au(0);
self.base.position.size.block = current_block_offset;
}
self.base.restyle_damage.remove(REFLOW_OUT_OF_FLOW | REFLOW);
}
}
/// Inline collapsed borders for the table itself.
struct TableInlineCollapsedBorders {
/// The table border at the start of the inline direction.
start: CollapsedBorder,
/// The table border at the end of the inline direction.
end: CollapsedBorder,
}
enum PreviousBlockCollapsedBorders {
FromPreviousRow(Vec<CollapsedBorder>),
FromTable(CollapsedBorder),
NotCollapsingBorders,
}
enum NextBlockCollapsedBorders<'a> {
FromNextRow(&'a [CollapsedBorder]),
FromTable(CollapsedBorder),
NotCollapsingBorders,
}

View file

@ -46,6 +46,10 @@ impl Flow for TableCaptionFlow {
&mut self.block_flow
}
fn as_immutable_block(&self) -> &BlockFlow {
&self.block_flow
}
fn bubble_inline_sizes(&mut self) {
self.block_flow.bubble_inline_sizes();
}
@ -105,3 +109,4 @@ impl fmt::Debug for TableCaptionFlow {
write!(f, "TableCaptionFlow: {:?}", self.block_flow)
}
}

View file

@ -8,28 +8,39 @@
use block::{BlockFlow, ISizeAndMarginsComputer, MarginsMayCollapseFlag};
use context::LayoutContext;
use css::node_style::StyledNode;
use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode};
use flow::{Flow, FlowClass};
use fragment::{Fragment, FragmentBorderBoxIterator};
use model::{MaybeAuto};
use model::MaybeAuto;
use layout_debug;
use table::InternalTable;
use table_row::{CollapsedBorder, CollapsedBorderProvenance};
use wrapper::ThreadSafeLayoutNode;
use geom::{Point2D, Rect};
use util::geometry::Au;
use util::logical_geometry::LogicalRect;
use cssparser::Color;
use geom::{Point2D, Rect, SideOffsets2D, Size2D};
use gfx::display_list::DisplayList;
use std::fmt;
use style::properties::ComputedValues;
use style::legacy::UnsignedIntegerAttribute;
use std::sync::Arc;
use style::computed_values::{border_collapse, border_top_style};
use style::legacy::UnsignedIntegerAttribute;
use style::properties::ComputedValues;
use util::geometry::Au;
use util::logical_geometry::{LogicalMargin, LogicalRect, WritingMode};
/// A table formatting context.
#[derive(RustcEncodable)]
pub struct TableCellFlow {
/// Data common to all block flows.
pub block_flow: BlockFlow,
/// Border collapse information for the cell.
pub collapsed_borders: CollapsedBordersForCell,
/// The column span of this cell.
pub column_span: u32,
/// Whether this cell is visible. If false, the value of `empty-cells` means that we must not
/// display this cell.
pub visible: bool,
@ -42,6 +53,7 @@ impl TableCellFlow {
-> TableCellFlow {
TableCellFlow {
block_flow: BlockFlow::from_node_and_fragment(node, fragment),
collapsed_borders: CollapsedBordersForCell::new(),
column_span: node.get_unsigned_integer_attribute(UnsignedIntegerAttribute::ColSpan)
.unwrap_or(1),
visible: visible,
@ -85,6 +97,10 @@ impl Flow for TableCellFlow {
&mut self.block_flow
}
fn as_immutable_block(&self) -> &BlockFlow {
&self.block_flow
}
/// Minimum/preferred inline-sizes set by this function are used in automatic table layout
/// calculation.
fn bubble_inline_sizes(&mut self) {
@ -120,10 +136,11 @@ impl Flow for TableCellFlow {
let containing_block_inline_size = self.block_flow.base.block_container_inline_size;
let inline_size_computer = InternalTable;
let border_collapse = self.block_flow.fragment.style.get_inheritedtable().border_collapse;
inline_size_computer.compute_used_inline_size(&mut self.block_flow,
layout_context,
containing_block_inline_size);
containing_block_inline_size,
border_collapse);
let inline_start_content_edge =
self.block_flow.fragment.border_box.start.i +
@ -140,7 +157,7 @@ impl Flow for TableCellFlow {
inline_start_content_edge,
inline_end_content_edge,
content_inline_size,
None);
|_, _, _, _, _| {});
}
fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
@ -161,9 +178,22 @@ impl Flow for TableCellFlow {
}
fn build_display_list(&mut self, layout_context: &LayoutContext) {
if self.visible {
self.block_flow.build_display_list(layout_context)
if !self.visible {
return
}
let border_painting_mode = match self.block_flow
.fragment
.style
.get_inheritedtable()
.border_collapse {
border_collapse::T::separate => BorderPaintingMode::Separate,
border_collapse::T::collapse => BorderPaintingMode::Collapse(&self.collapsed_borders),
};
self.block_flow.build_display_list_for_block(box DisplayList::new(),
layout_context,
border_painting_mode)
}
fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
@ -194,3 +224,134 @@ impl fmt::Debug for TableCellFlow {
write!(f, "TableCellFlow: {:?}", self.block_flow)
}
}
#[derive(Copy, Clone, Debug, RustcEncodable)]
pub struct CollapsedBordersForCell {
pub inline_start_border: CollapsedBorder,
pub inline_end_border: CollapsedBorder,
pub block_start_border: CollapsedBorder,
pub block_end_border: CollapsedBorder,
pub inline_start_width: Au,
pub inline_end_width: Au,
pub block_start_width: Au,
pub block_end_width: Au,
}
impl CollapsedBordersForCell {
fn new() -> CollapsedBordersForCell {
CollapsedBordersForCell {
inline_start_border: CollapsedBorder::new(),
inline_end_border: CollapsedBorder::new(),
block_start_border: CollapsedBorder::new(),
block_end_border: CollapsedBorder::new(),
inline_start_width: Au(0),
inline_end_width: Au(0),
block_start_width: Au(0),
block_end_width: Au(0),
}
}
fn should_paint_inline_start_border(&self) -> bool {
self.inline_start_border.provenance != CollapsedBorderProvenance::FromPreviousTableCell
}
fn should_paint_inline_end_border(&self) -> bool {
self.inline_end_border.provenance != CollapsedBorderProvenance::FromNextTableCell
}
fn should_paint_block_start_border(&self) -> bool {
self.block_start_border.provenance != CollapsedBorderProvenance::FromPreviousTableCell
}
fn should_paint_block_end_border(&self) -> bool {
self.block_end_border.provenance != CollapsedBorderProvenance::FromNextTableCell
}
pub fn adjust_border_widths_for_painting(&self, border_widths: &mut LogicalMargin<Au>) {
border_widths.inline_start = if !self.should_paint_inline_start_border() {
Au(0)
} else {
self.inline_start_border.width
};
border_widths.inline_end = if !self.should_paint_inline_end_border() {
Au(0)
} else {
self.inline_end_border.width
};
border_widths.block_start = if !self.should_paint_block_start_border() {
Au(0)
} else {
self.block_start_border.width
};
border_widths.block_end = if !self.should_paint_block_end_border() {
Au(0)
} else {
self.block_end_border.width
}
}
pub fn adjust_border_bounds_for_painting(&self,
border_bounds: &mut Rect<Au>,
writing_mode: WritingMode) {
let inline_start_divisor = if self.should_paint_inline_start_border() {
2
} else {
-2
};
let inline_start_offset = self.inline_start_width / 2 + self.inline_start_border.width /
inline_start_divisor;
let inline_end_divisor = if self.should_paint_inline_end_border() {
2
} else {
-2
};
let inline_end_offset = self.inline_end_width / 2 + self.inline_end_border.width /
inline_end_divisor;
let block_start_divisor = if self.should_paint_block_start_border() {
2
} else {
-2
};
let block_start_offset = self.block_start_width / 2 + self.block_start_border.width /
block_start_divisor;
let block_end_divisor = if self.should_paint_block_end_border() {
2
} else {
-2
};
let block_end_offset = self.block_end_width / 2 + self.block_end_border.width /
block_end_divisor;
// FIXME(pcwalton): Get the real container size.
let mut logical_bounds =
LogicalRect::from_physical(writing_mode, *border_bounds, Size2D(Au(0), Au(0)));
logical_bounds.start.i = logical_bounds.start.i - inline_start_offset;
logical_bounds.start.b = logical_bounds.start.b - block_start_offset;
logical_bounds.size.inline = logical_bounds.size.inline + inline_start_offset +
inline_end_offset;
logical_bounds.size.block = logical_bounds.size.block + block_start_offset +
block_end_offset;
*border_bounds = logical_bounds.to_physical(writing_mode, Size2D(Au(0), Au(0)))
}
pub fn adjust_border_colors_and_styles_for_painting(
&self,
border_colors: &mut SideOffsets2D<Color>,
border_styles: &mut SideOffsets2D<border_top_style::T>,
writing_mode: WritingMode) {
let logical_border_colors = LogicalMargin::new(writing_mode,
self.block_start_border.color,
self.inline_end_border.color,
self.block_end_border.color,
self.inline_start_border.color);
*border_colors = logical_border_colors.to_physical(writing_mode);
let logical_border_styles = LogicalMargin::new(writing_mode,
self.block_start_border.style,
self.inline_end_border.style,
self.block_end_border.style,
self.inline_start_border.style);
*border_styles = logical_border_styles.to_physical(writing_mode);
}
}

View file

@ -6,30 +6,35 @@
#![deny(unsafe_code)]
use block::BlockFlow;
use block::ISizeAndMarginsComputer;
use block::{BlockFlow, ISizeAndMarginsComputer};
use context::LayoutContext;
use flow::{self, FlowClass, Flow, ImmutableFlowUtils};
use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode};
use flow::{self, BaseFlow, FlowClass, Flow, ImmutableFlowUtils};
use flow_list::MutFlowListIterator;
use fragment::{Fragment, FragmentBorderBoxIterator};
use layout_debug;
use table::{ChildInlineSizeInfo, ColumnComputedInlineSize, ColumnIntrinsicInlineSize};
use table::{InternalTable};
use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, InternalTable, VecExt};
use table_cell::{CollapsedBordersForCell, TableCellFlow};
use model::MaybeAuto;
use wrapper::ThreadSafeLayoutNode;
use cssparser::{Color, RGBA};
use geom::{Point2D, Rect};
use util::geometry::Au;
use util::logical_geometry::LogicalRect;
use gfx::display_list::DisplayList;
use rustc_serialize::{Encoder, Encodable};
use std::cmp::max;
use std::fmt;
use std::iter::{Enumerate, IntoIterator, Peekable};
use std::sync::Arc;
use style::computed_values::border_spacing;
use style::computed_values::{border_collapse, border_spacing, border_top_style};
use style::properties::ComputedValues;
use style::values::computed::LengthOrPercentageOrAuto;
use util::geometry::Au;
use util::logical_geometry::{LogicalRect, WritingMode};
/// A single row of a table.
#[derive(RustcEncodable)]
pub struct TableRowFlow {
/// Fields common to all block flows.
pub block_flow: BlockFlow,
/// Information about the intrinsic inline-sizes of each cell.
@ -41,6 +46,23 @@ pub struct TableRowFlow {
/// The spacing for this row, propagated down from the table during the inline-size assignment
/// phase.
pub spacing: border_spacing::T,
/// Information about the borders for each cell that we bubble up to our parent. This is only
/// computed if `border-collapse` is `collapse`.
pub preliminary_collapsed_borders: CollapsedBordersForRow,
/// Information about the borders for each cell, post-collapse. This is only computed if
/// `border-collapse` is `collapse`.
pub final_collapsed_borders: CollapsedBordersForRow,
/// The computed cell spacing widths post-collapse.
pub collapsed_border_spacing: CollapsedBorderSpacingForRow,
}
impl Encodable for TableRowFlow {
fn encode<S: Encoder>(&self, e: &mut S) -> Result<(), S::Error> {
self.block_flow.encode(e)
}
}
/// Information about the column inline size and span for each cell.
@ -52,6 +74,7 @@ pub struct CellIntrinsicInlineSize {
pub column_span: u32,
}
impl TableRowFlow {
pub fn from_node_and_fragment(node: &ThreadSafeLayoutNode, fragment: Fragment)
-> TableRowFlow {
@ -63,6 +86,9 @@ impl TableRowFlow {
horizontal: Au(0),
vertical: Au(0),
},
preliminary_collapsed_borders: CollapsedBordersForRow::new(),
final_collapsed_borders: CollapsedBordersForRow::new(),
collapsed_border_spacing: CollapsedBorderSpacingForRow::new(),
}
}
@ -70,12 +96,6 @@ impl TableRowFlow {
&self.block_flow.fragment
}
fn initialize_offsets(&mut self) -> (Au, Au, Au) {
// TODO: If border-collapse: collapse, block_start_offset, block_end_offset, and
// inline_start_offset should be updated. Currently, they are set as Au(0).
(Au(0), Au(0), Au(0))
}
/// Assign block-size for table-row flow.
///
/// TODO(pcwalton): This doesn't handle floats and positioned elements right.
@ -84,8 +104,6 @@ impl TableRowFlow {
/// methods
#[inline(always)]
fn assign_block_size_table_row_base<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
let (block_start_offset, _, _) = self.initialize_offsets();
// Per CSS 2.1 § 17.5.3, find max_y = max(computed `block-size`, minimum block-size of all
// cells).
let mut max_block_size = Au(0);
@ -108,7 +126,7 @@ impl TableRowFlow {
child_fragment.border_padding.block_start_end());
}
let child_node = flow::mut_base(kid);
child_node.position.start.b = block_start_offset;
child_node.position.start.b = Au(0);
max_block_size = max(max_block_size, child_node.position.size.block);
}
@ -120,7 +138,7 @@ impl TableRowFlow {
.content_block_size(),
Au(0)) {
MaybeAuto::Auto => block_size,
MaybeAuto::Specified(value) => max(value, block_size)
MaybeAuto::Specified(value) => max(value, block_size),
};
// Assign the block-size of own fragment
@ -131,14 +149,38 @@ impl TableRowFlow {
// Assign the block-size of kid fragments, which is the same value as own block-size.
for kid in self.block_flow.base.child_iter() {
let child_table_cell = kid.as_table_cell();
{
let kid_fragment = kid.as_table_cell().mut_fragment();
let kid_fragment = child_table_cell.mut_fragment();
let mut position = kid_fragment.border_box;
position.size.block = block_size;
kid_fragment.border_box = position;
}
let child_node = flow::mut_base(kid);
child_node.position.size.block = block_size;
// Assign the child's block size.
child_table_cell.block_flow.base.position.size.block = block_size
}
}
pub fn populate_collapsed_border_spacing<'a,I>(
&mut self,
collapsed_inline_direction_border_widths_for_table: &[Au],
collapsed_block_direction_border_widths_for_table: &mut Peekable<I>)
where I: Iterator<Item=&'a Au> {
self.collapsed_border_spacing.inline.clear();
self.collapsed_border_spacing
.inline
.extend(collapsed_inline_direction_border_widths_for_table.into_iter().map(|x| *x));
if let Some(collapsed_block_direction_border_width_for_table) =
collapsed_block_direction_border_widths_for_table.next() {
self.collapsed_border_spacing.block_start =
*collapsed_block_direction_border_width_for_table
}
if let Some(collapsed_block_direction_border_width_for_table) =
collapsed_block_direction_border_widths_for_table.peek() {
self.collapsed_border_spacing.block_end =
**collapsed_block_direction_border_width_for_table
}
}
}
@ -160,6 +202,10 @@ impl Flow for TableRowFlow {
&mut self.block_flow
}
fn as_immutable_block(&self) -> &BlockFlow {
&self.block_flow
}
fn column_intrinsic_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<ColumnIntrinsicInlineSize> {
panic!("can't call column_intrinsic_inline_sizes() on table row")
}
@ -181,48 +227,73 @@ impl Flow for TableRowFlow {
// Bubble up the specified inline-sizes from child table cells.
let (mut min_inline_size, mut pref_inline_size) = (Au(0), Au(0));
for kid in self.block_flow.base.child_iter() {
assert!(kid.is_table_cell());
let collapsing_borders = self.block_flow
.fragment
.style()
.get_inheritedtable()
.border_collapse == border_collapse::T::collapse;
// FIXME(pcwalton): Shouldn't use `CollapsedBorder::new()` here.
self.preliminary_collapsed_borders.reset(CollapsedBorder::new());
// Collect the specified column inline-size of the cell. This is used in both fixed and
// automatic table layout calculation.
let child_specified_inline_size;
let child_column_span;
{
let child_table_cell = kid.as_table_cell();
child_specified_inline_size = child_table_cell.fragment()
.style()
.content_inline_size();
child_column_span = child_table_cell.column_span
}
{
let mut iterator = self.block_flow.base.child_iter().enumerate().peekable();
while let Some((i, kid)) = iterator.next() {
assert!(kid.is_table_cell());
// Collect minimum and preferred inline-sizes of the cell for automatic table layout
// calculation.
let child_base = flow::mut_base(kid);
let child_column_inline_size = ColumnIntrinsicInlineSize {
minimum_length: match child_specified_inline_size {
LengthOrPercentageOrAuto::Auto | LengthOrPercentageOrAuto::Percentage(_) => {
child_base.intrinsic_inline_sizes.minimum_inline_size
// Collect the specified column inline-size of the cell. This is used in both
// fixed and automatic table layout calculation.
let child_specified_inline_size;
let child_column_span;
{
let child_table_cell = kid.as_table_cell();
child_specified_inline_size = child_table_cell.block_flow
.fragment
.style
.content_inline_size();
child_column_span = child_table_cell.column_span;
// Perform border collapse if necessary.
if collapsing_borders {
perform_inline_direction_border_collapse_for_row(
i,
child_table_cell,
&mut iterator,
&mut self.preliminary_collapsed_borders)
}
LengthOrPercentageOrAuto::Length(length) => length,
},
percentage: match child_specified_inline_size {
LengthOrPercentageOrAuto::Auto | LengthOrPercentageOrAuto::Length(_) => 0.0,
LengthOrPercentageOrAuto::Percentage(percentage) => percentage,
},
preferred: child_base.intrinsic_inline_sizes.preferred_inline_size,
constrained: match child_specified_inline_size {
LengthOrPercentageOrAuto::Length(_) => true,
LengthOrPercentageOrAuto::Auto | LengthOrPercentageOrAuto::Percentage(_) => false,
},
};
min_inline_size = min_inline_size + child_column_inline_size.minimum_length;
pref_inline_size = pref_inline_size + child_column_inline_size.preferred;
self.cell_intrinsic_inline_sizes.push(CellIntrinsicInlineSize {
column_size: child_column_inline_size,
column_span: child_column_span,
});
}
// Collect minimum and preferred inline-sizes of the cell for automatic table layout
// calculation.
let child_base = flow::mut_base(kid);
let child_column_inline_size = ColumnIntrinsicInlineSize {
minimum_length: match child_specified_inline_size {
LengthOrPercentageOrAuto::Auto |
LengthOrPercentageOrAuto::Percentage(_) => {
child_base.intrinsic_inline_sizes.minimum_inline_size
}
LengthOrPercentageOrAuto::Length(length) => length,
},
percentage: match child_specified_inline_size {
LengthOrPercentageOrAuto::Auto |
LengthOrPercentageOrAuto::Length(_) => 0.0,
LengthOrPercentageOrAuto::Percentage(percentage) => percentage,
},
preferred: child_base.intrinsic_inline_sizes.preferred_inline_size,
constrained: match child_specified_inline_size {
LengthOrPercentageOrAuto::Length(_) => true,
LengthOrPercentageOrAuto::Auto |
LengthOrPercentageOrAuto::Percentage(_) => false,
},
};
min_inline_size = min_inline_size + child_column_inline_size.minimum_length;
pref_inline_size = pref_inline_size + child_column_inline_size.preferred;
self.cell_intrinsic_inline_sizes.push(CellIntrinsicInlineSize {
column_size: child_column_inline_size,
column_span: child_column_span,
});
}
}
self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size = min_inline_size;
self.block_flow.base.intrinsic_inline_sizes.preferred_inline_size = max(min_inline_size,
pref_inline_size);
@ -241,9 +312,11 @@ impl Flow for TableRowFlow {
let inline_end_content_edge = Au(0);
let inline_size_computer = InternalTable;
let border_collapse = self.block_flow.fragment.style.get_inheritedtable().border_collapse;
inline_size_computer.compute_used_inline_size(&mut self.block_flow,
layout_context,
containing_block_inline_size);
containing_block_inline_size,
border_collapse);
// Spread out the completed inline sizes among columns with spans > 1.
let mut computed_inline_size_for_cells = Vec::new();
@ -279,21 +352,44 @@ impl Flow for TableRowFlow {
computed_inline_size_for_cells.push(column_computed_inline_size)
}
// Set up border collapse info.
let border_collapse_info =
match self.block_flow.fragment.style().get_inheritedtable().border_collapse {
border_collapse::T::collapse => {
Some(BorderCollapseInfoForChildTableCell {
collapsed_borders_for_row: &self.final_collapsed_borders,
collapsed_border_spacing_for_row: &self.collapsed_border_spacing,
})
}
border_collapse::T::separate => None,
};
// Push those inline sizes down to the cells.
let info = ChildInlineSizeInfo {
column_computed_inline_sizes: &computed_inline_size_for_cells,
spacing: self.spacing,
};
let spacing = self.spacing;
self.block_flow.propagate_assigned_inline_size_to_children(layout_context,
inline_start_content_edge,
inline_end_content_edge,
containing_block_inline_size,
Some(info));
|child_flow,
child_index,
content_inline_size,
writing_mode,
inline_start_margin_edge| {
propagate_column_inline_sizes_to_child(
child_flow,
child_index,
content_inline_size,
writing_mode,
computed_inline_size_for_cells.as_slice(),
&spacing,
&border_collapse_info,
inline_start_margin_edge)
})
}
fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
fn assign_block_size<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
debug!("assign_block_size: assigning block_size for table_row");
self.assign_block_size_table_row_base(ctx);
self.assign_block_size_table_row_base(layout_context);
}
fn compute_absolute_position(&mut self) {
@ -309,7 +405,18 @@ impl Flow for TableRowFlow {
}
fn build_display_list(&mut self, layout_context: &LayoutContext) {
self.block_flow.build_display_list(layout_context)
let border_painting_mode = match self.block_flow
.fragment
.style
.get_inheritedtable()
.border_collapse {
border_collapse::T::separate => BorderPaintingMode::Separate,
border_collapse::T::collapse => BorderPaintingMode::Hidden,
};
self.block_flow.build_display_list_for_block(box DisplayList::new(),
layout_context,
border_painting_mode);
}
fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
@ -340,3 +447,375 @@ impl fmt::Debug for TableRowFlow {
write!(f, "TableRowFlow: {:?}", self.block_flow.fragment)
}
}
#[derive(Clone, Debug)]
pub struct CollapsedBordersForRow {
/// The size of this vector should be equal to the number of cells plus one.
pub inline: Vec<CollapsedBorder>,
/// The size of this vector should be equal to the number of cells.
pub block_start: Vec<CollapsedBorder>,
/// The size of this vector should be equal to the number of cells.
pub block_end: Vec<CollapsedBorder>,
}
impl CollapsedBordersForRow {
pub fn new() -> CollapsedBordersForRow {
CollapsedBordersForRow {
inline: Vec::new(),
block_start: Vec::new(),
block_end: Vec::new(),
}
}
pub fn reset(&mut self, first_inline_border: CollapsedBorder) {
self.inline.clear();
self.inline.push(first_inline_border);
self.block_start.clear();
self.block_end.clear()
}
}
#[derive(Clone, Debug)]
pub struct CollapsedBorderSpacingForRow {
/// The spacing in between each column.
inline: Vec<Au>,
/// The spacing above this row.
pub block_start: Au,
/// The spacing below this row.
block_end: Au,
}
impl CollapsedBorderSpacingForRow {
fn new() -> CollapsedBorderSpacingForRow {
CollapsedBorderSpacingForRow {
inline: Vec::new(),
block_start: Au(0),
block_end: Au(0),
}
}
}
/// All aspects of a border that can collapse with adjacent borders. See CSS 2.1 § 17.6.2.1.
#[derive(Copy, Clone, Debug)]
pub struct CollapsedBorder {
/// The style of the border.
pub style: border_top_style::T,
/// The width of the border.
pub width: Au,
/// The color of the border.
pub color: Color,
/// The type of item that this border comes from.
pub provenance: CollapsedBorderProvenance,
}
impl Encodable for CollapsedBorder {
fn encode<S: Encoder>(&self, _: &mut S) -> Result<(), S::Error> {
Ok(())
}
}
/// Where a border style comes from.
///
/// The integer values here correspond to the border conflict resolution rules in CSS 2.1 §
/// 17.6.2.1. Higher values override lower values.
#[derive(Copy, Clone, Debug, PartialEq, RustcEncodable)]
pub enum CollapsedBorderProvenance {
FromPreviousTableCell = 6,
FromNextTableCell = 5,
FromTableRow = 4,
FromTableRowGroup = 3,
FromTableColumn = 2,
FromTableColumnGroup = 1,
FromTable = 0,
}
impl CollapsedBorder {
/// Creates a collapsible border style for no border.
pub fn new() -> CollapsedBorder {
CollapsedBorder {
style: border_top_style::T::none,
width: Au(0),
color: Color::RGBA(RGBA {
red: 0.0,
green: 0.0,
blue: 0.0,
alpha: 0.0,
}),
provenance: CollapsedBorderProvenance::FromTable,
}
}
/// Creates a collapsed border from the block-start border described in the given CSS style
/// object.
fn top(css_style: &ComputedValues, provenance: CollapsedBorderProvenance)
-> CollapsedBorder {
CollapsedBorder {
style: css_style.get_border().border_top_style,
width: css_style.get_border().border_top_width,
color: css_style.get_border().border_top_color,
provenance: provenance,
}
}
/// Creates a collapsed border style from the right border described in the given CSS style
/// object.
fn right(css_style: &ComputedValues, provenance: CollapsedBorderProvenance)
-> CollapsedBorder {
CollapsedBorder {
style: css_style.get_border().border_right_style,
width: css_style.get_border().border_right_width,
color: css_style.get_border().border_right_color,
provenance: provenance,
}
}
/// Creates a collapsed border style from the bottom border described in the given CSS style
/// object.
fn bottom(css_style: &ComputedValues, provenance: CollapsedBorderProvenance)
-> CollapsedBorder {
CollapsedBorder {
style: css_style.get_border().border_bottom_style,
width: css_style.get_border().border_bottom_width,
color: css_style.get_border().border_bottom_color,
provenance: provenance,
}
}
/// Creates a collapsed border style from the left border described in the given CSS style
/// object.
fn left(css_style: &ComputedValues, provenance: CollapsedBorderProvenance)
-> CollapsedBorder {
CollapsedBorder {
style: css_style.get_border().border_left_style,
width: css_style.get_border().border_left_width,
color: css_style.get_border().border_left_color,
provenance: provenance,
}
}
/// Creates a collapsed border style from the inline-start border described in the given CSS
/// style object.
pub fn inline_start(css_style: &ComputedValues, provenance: CollapsedBorderProvenance)
-> CollapsedBorder {
let writing_mode = css_style.writing_mode;
match (writing_mode.is_vertical(),
writing_mode.is_inline_tb(),
writing_mode.is_bidi_ltr()) {
(false, _, true) => CollapsedBorder::left(css_style, provenance),
(false, _, false) => CollapsedBorder::right(css_style, provenance),
(true, true, _) => CollapsedBorder::top(css_style, provenance),
(true, false, _) => CollapsedBorder::bottom(css_style, provenance),
}
}
/// Creates a collapsed border style from the inline-start border described in the given CSS
/// style object.
pub fn inline_end(css_style: &ComputedValues, provenance: CollapsedBorderProvenance)
-> CollapsedBorder {
let writing_mode = css_style.writing_mode;
match (writing_mode.is_vertical(),
writing_mode.is_inline_tb(),
writing_mode.is_bidi_ltr()) {
(false, _, true) => CollapsedBorder::right(css_style, provenance),
(false, _, false) => CollapsedBorder::left(css_style, provenance),
(true, true, _) => CollapsedBorder::bottom(css_style, provenance),
(true, false, _) => CollapsedBorder::top(css_style, provenance),
}
}
/// Creates a collapsed border style from the block-start border described in the given CSS
/// style object.
pub fn block_start(css_style: &ComputedValues, provenance: CollapsedBorderProvenance)
-> CollapsedBorder {
let writing_mode = css_style.writing_mode;
match (writing_mode.is_vertical(), writing_mode.is_vertical_lr()) {
(false, _) => CollapsedBorder::top(css_style, provenance),
(true, true) => CollapsedBorder::left(css_style, provenance),
(true, false) => CollapsedBorder::right(css_style, provenance),
}
}
/// Creates a collapsed border style from the block-end border described in the given CSS style
/// object.
pub fn block_end(css_style: &ComputedValues, provenance: CollapsedBorderProvenance)
-> CollapsedBorder {
let writing_mode = css_style.writing_mode;
match (writing_mode.is_vertical(), writing_mode.is_vertical_lr()) {
(false, _) => CollapsedBorder::bottom(css_style, provenance),
(true, true) => CollapsedBorder::right(css_style, provenance),
(true, false) => CollapsedBorder::left(css_style, provenance),
}
}
/// If `other` has a higher priority per CSS 2.1 § 17.6.2.1, replaces `self` with it.
pub fn combine(&mut self, other: &CollapsedBorder) {
match (self.style, other.style) {
// Step 1.
(border_top_style::T::hidden, _) => {}
(_, border_top_style::T::hidden) => *self = *other,
// Step 2.
(border_top_style::T::none, _) => *self = *other,
(_, border_top_style::T::none) => {}
// Step 3.
_ if self.width > other.width => {}
_ if self.width < other.width => *self = *other,
(this_style, other_style) if (this_style as i8) > other_style as i8 => {}
(this_style, other_style) if (this_style as i8) < other_style as i8 => *self = *other,
// Step 4.
_ if (self.provenance as i8) >= other.provenance as i8 => {}
_ => *self = *other,
}
}
}
/// Pushes column inline size and border collapse info down to a child.
pub fn propagate_column_inline_sizes_to_child(
child_flow: &mut Flow,
child_index: usize,
content_inline_size: Au,
writing_mode: WritingMode,
column_computed_inline_sizes: &[ColumnComputedInlineSize],
border_spacing: &border_spacing::T,
border_collapse_info: &Option<BorderCollapseInfoForChildTableCell>,
inline_start_margin_edge: &mut Au) {
// If the child is a row group or a row, the column inline-size info should be copied from its
// parent.
//
// FIXME(pcwalton): This seems inefficient. Reference count it instead?
let inline_size = match child_flow.class() {
FlowClass::Table => {
let child_table_flow = child_flow.as_table();
child_table_flow.column_computed_inline_sizes = column_computed_inline_sizes.to_vec();
content_inline_size
}
FlowClass::TableRowGroup => {
let child_table_rowgroup_flow = child_flow.as_table_rowgroup();
child_table_rowgroup_flow.column_computed_inline_sizes =
column_computed_inline_sizes.to_vec();
child_table_rowgroup_flow.spacing = *border_spacing;
content_inline_size
}
FlowClass::TableRow => {
let child_table_row_flow = child_flow.as_table_row();
child_table_row_flow.column_computed_inline_sizes =
column_computed_inline_sizes.to_vec();
child_table_row_flow.spacing = *border_spacing;
content_inline_size
}
FlowClass::TableCell => column_computed_inline_sizes[child_index].size,
_ => content_inline_size,
};
if !child_flow.is_table_cell() {
set_inline_position_of_child_flow(flow::mut_base(child_flow),
inline_start_margin_edge,
inline_size,
writing_mode);
return
}
// Handle border collapsing, if necessary.
let child_table_cell = child_flow.as_table_cell();
match *border_collapse_info {
Some(ref border_collapse_info) => {
// Write in the child's border collapse state.
child_table_cell.collapsed_borders = CollapsedBordersForCell {
inline_start_border: border_collapse_info.collapsed_borders_for_row
.inline
.get(child_index)
.map(|x| *x)
.unwrap_or(CollapsedBorder::new()),
inline_end_border: border_collapse_info.collapsed_borders_for_row
.inline
.get(child_index + 1)
.map(|x| *x)
.unwrap_or(CollapsedBorder::new()),
block_start_border: border_collapse_info.collapsed_borders_for_row
.block_start
.get(child_index)
.map(|x| *x)
.unwrap_or(CollapsedBorder::new()),
block_end_border: border_collapse_info.collapsed_borders_for_row
.block_end
.get(child_index)
.map(|x| *x)
.unwrap_or(CollapsedBorder::new()),
inline_start_width: border_collapse_info.collapsed_border_spacing_for_row
.inline
.get(child_index)
.map(|x| *x)
.unwrap_or(Au(0)),
inline_end_width: border_collapse_info.collapsed_border_spacing_for_row
.inline
.get(child_index + 1)
.map(|x| *x)
.unwrap_or(Au(0)),
block_start_width: border_collapse_info.collapsed_border_spacing_for_row
.block_start,
block_end_width: border_collapse_info.collapsed_border_spacing_for_row.block_end,
};
// Move over past the collapsed border.
*inline_start_margin_edge = *inline_start_margin_edge +
child_table_cell.collapsed_borders.inline_start_width
}
None => {
// Take spacing into account.
*inline_start_margin_edge = *inline_start_margin_edge + border_spacing.horizontal
}
}
set_inline_position_of_child_flow(&mut child_table_cell.block_flow.base,
inline_start_margin_edge,
inline_size,
writing_mode);
*inline_start_margin_edge = *inline_start_margin_edge + inline_size
}
#[derive(Copy, Clone)]
pub struct BorderCollapseInfoForChildTableCell<'a> {
collapsed_borders_for_row: &'a CollapsedBordersForRow,
collapsed_border_spacing_for_row: &'a CollapsedBorderSpacingForRow,
}
fn set_inline_position_of_child_flow(child_flow: &mut BaseFlow,
inline_start_margin_edge: &mut Au,
inline_size: Au,
writing_mode: WritingMode) {
child_flow.position.start.i = *inline_start_margin_edge;
child_flow.block_container_inline_size = inline_size;
child_flow.block_container_writing_mode = writing_mode;
}
/// Performs border-collapse in the inline direction for all the cells' inside borders in the
/// inline-direction cells and propagates the outside borders (the far left and right) up to the
/// table row. This is done eagerly here so that at least the inline inside border collapse
/// computations can be parallelized across all the rows of the table.
fn perform_inline_direction_border_collapse_for_row(
child_index: usize,
child_table_cell: &mut TableCellFlow,
iterator: &mut Peekable<Enumerate<MutFlowListIterator>>,
preliminary_collapsed_borders: &mut CollapsedBordersForRow) {
let inline_collapsed_border = preliminary_collapsed_borders.inline.push_or_mutate(
child_index + 1,
CollapsedBorder::inline_end(&*child_table_cell.block_flow.fragment.style,
CollapsedBorderProvenance::FromPreviousTableCell));
if let Some(&(_, ref next_child_flow)) = iterator.peek() {
let next_child_flow = next_child_flow.as_immutable_block();
inline_collapsed_border.combine(
&CollapsedBorder::inline_start(&*next_child_flow.fragment.style,
CollapsedBorderProvenance::FromNextTableCell))
};
let block_start_border =
CollapsedBorder::block_start(&*child_table_cell.block_flow.fragment.style,
CollapsedBorderProvenance::FromNextTableCell);
preliminary_collapsed_borders.block_start.push_or_mutate(child_index, block_start_border);
let block_end_border =
CollapsedBorder::block_end(&*child_table_cell.block_flow.fragment.style,
CollapsedBorderProvenance::FromPreviousTableCell);
preliminary_collapsed_borders.block_end.push_or_mutate(child_index, block_end_border);
}

View file

@ -11,20 +11,21 @@ use context::LayoutContext;
use flow::{FlowClass, Flow};
use fragment::{Fragment, FragmentBorderBoxIterator};
use layout_debug;
use style::computed_values::border_spacing;
use table::{ChildInlineSizeInfo, ColumnComputedInlineSize, ColumnIntrinsicInlineSize};
use table::{InternalTable, TableLikeFlow};
use style::computed_values::{border_collapse, border_spacing};
use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, InternalTable, TableLikeFlow};
use table_row::{self, CollapsedBordersForRow};
use wrapper::ThreadSafeLayoutNode;
use geom::{Point2D, Rect};
use rustc_serialize::{Encoder, Encodable};
use std::fmt;
use std::iter::{IntoIterator, Iterator, Peekable};
use std::sync::Arc;
use style::properties::ComputedValues;
use util::geometry::Au;
use util::logical_geometry::LogicalRect;
use std::fmt;
use style::properties::ComputedValues;
use std::sync::Arc;
/// A table formatting context.
#[derive(RustcEncodable)]
pub struct TableRowGroupFlow {
/// Fields common to all block flows.
pub block_flow: BlockFlow,
@ -37,6 +38,24 @@ pub struct TableRowGroupFlow {
/// The spacing for this rowgroup.
pub spacing: border_spacing::T,
/// Information about the borders for each cell that we bubble up to our parent. This is only
/// computed if `border-collapse` is `collapse`.
pub preliminary_collapsed_borders: CollapsedBordersForRow,
/// The final width of the borders in the inline direction for each cell, computed by the
/// entire table and pushed down into each row during inline size computation.
pub collapsed_inline_direction_border_widths_for_table: Vec<Au>,
/// The final width of the borders in the block direction for each cell, computed by the
/// entire table and pushed down into each row during inline size computation.
pub collapsed_block_direction_border_widths_for_table: Vec<Au>,
}
impl Encodable for TableRowGroupFlow {
fn encode<S: Encoder>(&self, e: &mut S) -> Result<(), S::Error> {
self.block_flow.encode(e)
}
}
impl TableRowGroupFlow {
@ -50,12 +69,38 @@ impl TableRowGroupFlow {
horizontal: Au(0),
vertical: Au(0),
},
preliminary_collapsed_borders: CollapsedBordersForRow::new(),
collapsed_inline_direction_border_widths_for_table: Vec::new(),
collapsed_block_direction_border_widths_for_table: Vec::new(),
}
}
pub fn fragment<'a>(&'a mut self) -> &'a Fragment {
&self.block_flow.fragment
}
pub fn populate_collapsed_border_spacing<'a,I>(
&mut self,
collapsed_inline_direction_border_widths_for_table: &[Au],
collapsed_block_direction_border_widths_for_table: &mut Peekable<I>)
where I: Iterator<Item=&'a Au> {
self.collapsed_inline_direction_border_widths_for_table.clear();
self.collapsed_inline_direction_border_widths_for_table
.extend(collapsed_inline_direction_border_widths_for_table.into_iter().map(|x| *x));
for _ in range(0, self.block_flow.base.children.len()) {
if let Some(collapsed_block_direction_border_width_for_table) =
collapsed_block_direction_border_widths_for_table.next() {
self.collapsed_block_direction_border_widths_for_table
.push(*collapsed_block_direction_border_width_for_table)
}
}
if let Some(collapsed_block_direction_border_width_for_table) =
collapsed_block_direction_border_widths_for_table.peek() {
self.collapsed_block_direction_border_widths_for_table
.push(**collapsed_block_direction_border_width_for_table)
}
}
}
impl Flow for TableRowGroupFlow {
@ -75,6 +120,10 @@ impl Flow for TableRowGroupFlow {
&mut self.block_flow
}
fn as_immutable_block(&self) -> &BlockFlow {
&self.block_flow
}
fn column_intrinsic_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<ColumnIntrinsicInlineSize> {
&mut self.column_intrinsic_inline_sizes
}
@ -99,26 +148,48 @@ impl Flow for TableRowGroupFlow {
// The position was set to the containing block by the flow's parent.
let containing_block_inline_size = self.block_flow.base.block_container_inline_size;
// FIXME: In case of border-collapse: collapse, inline-start_content_edge should be
// the border width on the inline-start side.
let inline_start_content_edge = Au::new(0);
let inline_end_content_edge = Au::new(0);
let (inline_start_content_edge, inline_end_content_edge) = (Au(0), Au(0));
let content_inline_size = containing_block_inline_size;
let inline_size_computer = InternalTable;
let border_collapse = self.block_flow.fragment.style.get_inheritedtable().border_collapse;
inline_size_computer.compute_used_inline_size(&mut self.block_flow,
layout_context,
containing_block_inline_size);
containing_block_inline_size,
border_collapse);
let info = ChildInlineSizeInfo {
column_computed_inline_sizes: &self.column_computed_inline_sizes,
spacing: self.spacing,
};
let column_computed_inline_sizes = self.column_computed_inline_sizes.as_slice();
let border_spacing = self.spacing;
let collapsed_inline_direction_border_widths_for_table =
self.collapsed_inline_direction_border_widths_for_table.as_slice();
let mut collapsed_block_direction_border_widths_for_table =
self.collapsed_block_direction_border_widths_for_table.iter().peekable();
self.block_flow.propagate_assigned_inline_size_to_children(layout_context,
inline_start_content_edge,
inline_end_content_edge,
content_inline_size,
Some(info));
|child_flow,
child_index,
content_inline_size,
writing_mode,
inline_start_margin_edge| {
table_row::propagate_column_inline_sizes_to_child(
child_flow,
child_index,
content_inline_size,
writing_mode,
column_computed_inline_sizes,
&border_spacing,
&None,
inline_start_margin_edge);
if border_collapse == border_collapse::T::collapse {
let child_table_row = child_flow.as_table_row();
child_table_row.populate_collapsed_border_spacing(
collapsed_inline_direction_border_widths_for_table.as_slice(),
&mut collapsed_block_direction_border_widths_for_table);
}
});
}
fn assign_block_size<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {

View file

@ -20,11 +20,13 @@ use floats::FloatKind;
use flow::{FlowClass, Flow, ImmutableFlowUtils};
use flow::{IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS};
use fragment::{Fragment, FragmentBorderBoxIterator};
use table::{ChildInlineSizeInfo, ColumnComputedInlineSize, ColumnIntrinsicInlineSize};
use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize};
use table_row;
use wrapper::ThreadSafeLayoutNode;
use geom::{Point2D, Rect};
use util::geometry::Au;
use util::logical_geometry::LogicalRect;
use std::cmp::{max, min};
use std::fmt;
use std::ops::Add;
@ -98,25 +100,32 @@ impl TableWrapperFlow {
// when normally the child computes it itself. But it has to be this way because the
// padding will affect where we place the child. This is an odd artifact of the way that
// tables are separated into table flows and table wrapper flows.
//
// FIXME(pcwalton): Handle `border-collapse` correctly.
let mut available_inline_size = self.block_flow.fragment.border_box.size.inline;
let (mut table_border_padding, mut spacing) = (Au(0), Au(0));
for kid in self.block_flow.base.child_iter() {
if kid.is_table() {
let kid_block = kid.as_block();
let spacing_per_cell = kid_block.fragment
.style()
.get_inheritedtable()
.border_spacing
.horizontal;
spacing = spacing_per_cell * (self.column_intrinsic_inline_sizes.len() as i32 + 1);
available_inline_size = self.block_flow.fragment.border_box.size.inline;
kid_block.fragment.compute_border_and_padding(available_inline_size);
kid_block.fragment.compute_block_direction_margins(available_inline_size);
kid_block.fragment.compute_inline_direction_margins(available_inline_size);
table_border_padding = kid_block.fragment.border_padding.inline_start_end();
break
if !kid.is_table() {
continue
}
let kid_table = kid.as_table();
let spacing_per_cell = kid_table.spacing().horizontal;
spacing = spacing_per_cell * (self.column_intrinsic_inline_sizes.len() as i32 + 1);
available_inline_size = self.block_flow.fragment.border_box.size.inline;
let kid_block_flow = &mut kid_table.block_flow;
kid_block_flow.fragment
.compute_border_and_padding(available_inline_size,
self.block_flow
.fragment
.style
.get_inheritedtable()
.border_collapse);
kid_block_flow.fragment.compute_block_direction_margins(available_inline_size);
kid_block_flow.fragment.compute_inline_direction_margins(available_inline_size);
table_border_padding = kid_block_flow.fragment.border_padding.inline_start_end();
break
}
// FIXME(pcwalton, spec): INTRINSIC § 8 does not properly define how to compute this, but
@ -196,14 +205,17 @@ impl TableWrapperFlow {
layout_context: &LayoutContext,
parent_flow_inline_size: Au) {
// Delegate to the appropriate inline size computer to find the constraint inputs.
let border_collapse = self.block_flow.fragment.style.get_inheritedtable().border_collapse;
let input = if self.block_flow.base.flags.is_float() {
FloatNonReplaced.compute_inline_size_constraint_inputs(&mut self.block_flow,
parent_flow_inline_size,
layout_context)
layout_context,
border_collapse)
} else {
BlockNonReplaced.compute_inline_size_constraint_inputs(&mut self.block_flow,
parent_flow_inline_size,
layout_context)
layout_context,
border_collapse)
};
// Delegate to the appropriate inline size computer to write the constraint solutions in.
@ -313,26 +325,37 @@ impl Flow for TableWrapperFlow {
}
};
let border_spacing = self.block_flow.fragment.style().get_inheritedtable().border_spacing;
match assigned_column_inline_sizes {
None => {
self.block_flow.propagate_assigned_inline_size_to_children(
layout_context,
inline_start_content_edge,
inline_end_content_edge,
content_inline_size,
None)
}
Some(ref assigned_column_inline_sizes) => {
let info = ChildInlineSizeInfo {
column_computed_inline_sizes: &assigned_column_inline_sizes,
spacing: self.block_flow.fragment.style().get_inheritedtable().border_spacing,
};
self.block_flow
.propagate_assigned_inline_size_to_children(layout_context,
inline_start_content_edge,
inline_end_content_edge,
content_inline_size,
Some(info));
|_, _, _, _, _| {})
}
Some(ref assigned_column_inline_sizes) => {
self.block_flow
.propagate_assigned_inline_size_to_children(layout_context,
inline_start_content_edge,
inline_end_content_edge,
content_inline_size,
|child_flow,
child_index,
content_inline_size,
writing_mode,
inline_start_margin_edge| {
table_row::propagate_column_inline_sizes_to_child(
child_flow,
child_index,
content_inline_size,
writing_mode,
assigned_column_inline_sizes.as_slice(),
&border_spacing,
&None,
inline_start_margin_edge)
})
}
}
@ -369,6 +392,10 @@ impl Flow for TableWrapperFlow {
self.block_flow.update_late_computed_block_position_if_necessary(block_position)
}
fn generated_containing_block_rect(&self) -> LogicalRect<Au> {
self.block_flow.generated_containing_block_rect()
}
fn build_display_list(&mut self, layout_context: &LayoutContext) {
self.block_flow.build_display_list(layout_context)
}

View file

@ -41,7 +41,6 @@ partial interface CSSStyleDeclaration {
[TreatNullAs=EmptyString] attribute DOMString backgroundSize;
[TreatNullAs=EmptyString] attribute DOMString border;
[TreatNullAs=EmptyString] attribute DOMString borderCollapse;
[TreatNullAs=EmptyString] attribute DOMString borderColor;
[TreatNullAs=EmptyString] attribute DOMString borderRadius;
[TreatNullAs=EmptyString] attribute DOMString borderSpacing;
@ -119,6 +118,7 @@ partial interface CSSStyleDeclaration {
[TreatNullAs=EmptyString] attribute DOMString overflowWrap;
[TreatNullAs=EmptyString] attribute DOMString tableLayout;
[TreatNullAs=EmptyString] attribute DOMString borderCollapse;
[TreatNullAs=EmptyString] attribute DOMString emptyCells;
[TreatNullAs=EmptyString] attribute DOMString captionSide;

View file

@ -2020,12 +2020,12 @@ pub mod longhands {
${new_style_struct("InheritedTable", is_inherited=True)}
${single_keyword("border-collapse", "separate collapse")}
${single_keyword("empty-cells", "show hide")}
${single_keyword("caption-side", "top bottom")}
${single_keyword("border-collapse", "separate collapse", experimental=True)}
<%self:longhand name="border-spacing">
use values::computed::{Context, ToComputedValue};

View file

@ -6,14 +6,13 @@
pub use cssparser::RGBA;
macro_rules! define_css_keyword_enum {
($name: ident: $( $css: expr => $variant: ident ),+,) => {
define_css_keyword_enum!($name: $( $css => $variant ),+);
};
($name: ident: $( $css: expr => $variant: ident ),+) => {
#[allow(non_camel_case_types)]
#[derive(Clone, Eq, PartialEq, FromPrimitive, Copy, Hash)]
#[derive(Clone, Eq, PartialEq, FromPrimitive, Copy, Hash, RustcEncodable)]
pub enum $name {
$( $variant ),+
}
@ -46,6 +45,45 @@ macro_rules! define_css_keyword_enum {
}
}
macro_rules! define_numbered_css_keyword_enum {
($name: ident: $( $css: expr => $variant: ident = $value: expr ),+,) => {
define_numbered_css_keyword_enum!($name: $( $css => $variant = $value ),+);
};
($name: ident: $( $css: expr => $variant: ident = $value: expr ),+) => {
#[allow(non_camel_case_types)]
#[derive(Clone, Eq, PartialEq, FromPrimitive, Copy, RustcEncodable)]
pub enum $name {
$( $variant = $value ),+
}
impl $name {
pub fn parse(input: &mut ::cssparser::Parser) -> Result<$name, ()> {
match_ignore_ascii_case! { try!(input.expect_ident()),
$( $css => Ok($name::$variant) ),+
_ => Err(())
}
}
}
impl ::std::fmt::Debug for $name {
#[inline]
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
use cssparser::ToCss;
self.fmt_to_css(f)
}
}
impl ::cssparser::ToCss for $name {
fn to_css<W>(&self, dest: &mut W) -> ::text_writer::Result
where W: ::text_writer::TextWriter {
match self {
$( &$name::$variant => dest.write_str($css) ),+
}
}
}
}
}
pub type CSSFloat = f64;
@ -850,17 +888,19 @@ pub mod specified {
})
}
define_css_keyword_enum! { BorderStyle:
"none" => none,
"solid" => solid,
"double" => double,
"dotted" => dotted,
"dashed" => dashed,
"hidden" => hidden,
"groove" => groove,
"ridge" => ridge,
"inset" => inset,
"outset" => outset,
// The integer values here correspond to the border conflict resolution rules in CSS 2.1 §
// 17.6.2.1. Higher values override lower values.
define_numbered_css_keyword_enum! { BorderStyle:
"none" => none = -1,
"solid" => solid = 6,
"double" => double = 7,
"dotted" => dotted = 4,
"dashed" => dashed = 5,
"hidden" => hidden = -2,
"groove" => groove = 1,
"ridge" => ridge = 3,
"inset" => inset = 0,
"outset" => outset = 2,
}
/// A time in seconds according to CSS-VALUES § 6.2.

View file

@ -245,6 +245,20 @@ impl Au {
NumCast::from(px.get() * 60f32).unwrap()
}
/// Rounds this app unit down to the previous (left or top) pixel and returns it.
#[inline]
pub fn to_prev_px(&self) -> isize {
let Au(s) = *self;
((s as f64) / 60f64).floor() as isize
}
/// Rounds this app unit up to the next (right or bottom) pixel and returns it.
#[inline]
pub fn to_next_px(&self) -> isize {
let Au(s) = *self;
((s as f64) / 60f64).ceil() as isize
}
#[inline]
pub fn to_nearest_px(&self) -> isize {
let Au(s) = *self;

View file

@ -0,0 +1,54 @@
<!DOCTYPE html>
<html>
<head>
<style>
table {
border-collapse: collapse;
border: 5px solid yellow;
}
td {
border: 1px solid red;
padding: 16px;
width: 32px;
height: 32px;
}
td.cell5 {
border: 30px solid blue;
}
td.cell6 {
border: 5px solid green;
}
</style>
</head>
<body>
<table>
<tr id="row1">
<td> 1
<td> 2
<td> 3
</tr>
<tr id="row2">
<td> 4
<td class="cell5"> 5
<td class="cell6"> 6
</tr>
<tr id="row3">
<td> 7
<td> 8
<td> 9
</tr>
<tr id="row4">
<td> 10
<td> 11
<td> 12
</tr>
<tr id="row5">
<td> 13
<td> 14
<td> 15
</tr>
</table>
</body>
</html>

View file

@ -81,6 +81,7 @@ flaky_cpu == append_style_a.html append_style_b.html
!= border_black_ridge.html border_black_groove.html
!= border_black_ridge.html border_black_solid.html
== border_code_tag.html border_code_tag_ref.html
== border_collapse_simple_a.html border_collapse_simple_ref.html
== border_radius_clip_a.html border_radius_clip_ref.html
== border_radius_overlapping_a.html border_radius_overlapping_ref.html
== border_spacing_a.html border_spacing_ref.html

View file

@ -0,0 +1,72 @@
<!DOCTYPE html>
<html>
<head>
<!--
Tests that border collapse override and border collapse positioning works. All border widths
are even numbers to avoid subpixel rounding issues (the handling of which is not spec'd).
FIXME(pcwalton): This is currently offset by -2px in block and inline directions because we
don't correctly handle collapsed borders when calculating `table_border_padding` in
`table_wrapper.rs`.
-->
<style>
html, body {
margin: 0;
}
html {
padding: 0;
}
body {
/* See `FIXME` above. */
padding: 2px;
}
table {
border-collapse: collapse;
border: 4px solid black;
}
td {
border: 2px solid black;
padding: 16px;
width: 32px;
height: 32px;
}
td.cell5 {
border: 30px solid black;
}
td.cell6 {
border: 4px solid black;
}
</style>
</head>
<body>
<table>
<tr id="row1">
<td>
<td>
<td>
</tr>
<tr id="row2">
<td>
<td class="cell5">
<td class="cell6">
</tr>
<tr id="row3">
<td>
<td>
<td>
</tr>
<tr id="row4">
<td>
<td>
<td>
</tr>
<tr id="row5">
<td>
<td>
<td>
</tr>
</table>
</body>
</html>

View file

@ -0,0 +1,136 @@
<!DOCTYPE html>
<html>
<head>
<style>
html, body {
margin: 0;
padding: 0;
}
div {
box-sizing: border-box;
border-style: solid;
border-color: black;
position: absolute;
}
#c00 {
border-width: 4px 2px 2px 4px;
left: 0px;
top: 0px;
width: 82px;
height: 82px;
}
#c01 {
border-width: 4px 2px 0 0;
left: 82px;
top: 0px;
width: 94px;
height: 66px;
}
#c02 {
border-width: 4px 4px 0 0;
left: 176px;
top: 0px;
width: 82px;
height: 79px;
}
#c10 {
border-width: 0 0 2px 4px;
left: 0px;
top: 82px;
width: 66px;
height: 94px;
}
#c11 {
border-width: 30px;
left: 66px;
top: 66px;
width: 124px;
height: 124px;
}
#c12 {
border-width: 4px 4px 4px 0;
left: 190px;
top: 79px;
width: 68px;
height: 98px;
}
#c20 {
border-width: 0 2px 2px 4px;
left: 0px;
top: 176px;
width: 82px;
height: 80px;
}
#c21 {
border-width: 0 2px 2px 0;
left: 82px;
top: 190px;
width: 94px;
height: 66px;
}
#c22 {
border-width: 0 4px 2px 0;
left: 176px;
top: 177px;
width: 82px;
height: 79px;
}
#c30 {
border-width: 0 2px 2px 4px;
left: 0px;
top: 256px;
width: 82px;
height: 66px;
}
#c31 {
border-width: 0 2px 2px 0;
left: 82px;
top: 256px;
width: 94px;
height: 66px;
}
#c32 {
border-width: 0 4px 2px 0;
left: 176px;
top: 256px;
width: 82px;
height: 66px;
}
#c40 {
border-width: 0 2px 4px 4px;
left: 0px;
top: 322px;
width: 82px;
height: 68px;
}
#c41 {
border-width: 0 2px 4px 0;
left: 82px;
top: 322px;
width: 94px;
height: 68px;
}
#c42 {
border-width: 0 4px 4px 0;
left: 176px;
top: 322px;
width: 82px;
height: 68px;
}
</style>
</head>
<body>
<div id=c00></div><div id=c01></div><div id=c02></div>
<div id=c10></div><div id=c11></div><div id=c12></div>
<div id=c20></div><div id=c21></div><div id=c22></div>
<div id=c30></div><div id=c31></div><div id=c32></div>
<div id=c40></div><div id=c41></div><div id=c42></div>
</body>
</html>

View file

@ -32,5 +32,9 @@
}
</style>
</head>
<body><div class="bg"><span class="red">X</span><img src="400x400_green.png"><span class="green ib">X</span></div></body>
<body>
<div class="bg">
<span class="red">X</span><img src="400x400_green.png"><span class="green ib">X</span>
</div>
</body>
</html>