mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
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:
parent
92359c7b9a
commit
48299a53cb
22 changed files with 1975 additions and 520 deletions
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue