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

@ -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,
}