mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00: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
|
@ -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");
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 |
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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>) {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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};
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
54
tests/html/border_collapse_test.html
Normal file
54
tests/html/border_collapse_test.html
Normal 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>
|
||||
|
|
@ -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
|
||||
|
|
72
tests/ref/border_collapse_simple_a.html
Normal file
72
tests/ref/border_collapse_simple_a.html
Normal 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>
|
||||
|
136
tests/ref/border_collapse_simple_ref.html
Normal file
136
tests/ref/border_collapse_simple_ref.html
Normal 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>
|
||||
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue