mirror of
https://github.com/servo/servo.git
synced 2025-07-24 15:50:21 +01:00
layout: Implement border-spacing
per CSS 2.1 § 17.6.1 and the legacy
`cellspacing` attribute per HTML5 § 14.3.9. Table layout code has been refactored to push the spacing down to rowgroups and rows; this will aid the implementation of `border-collapse` as well. This commit also fixes two nasty issues in table layout: * In fixed layout, extra space would not be divided among columns that had auto width but had nonzero minimum width. * In automatic layout, extra space would be distributed to constrained columns as well even if unconstrained columns with percentage equal to zero were present.
This commit is contained in:
parent
e8f1a046c6
commit
586c12ccc4
26 changed files with 690 additions and 208 deletions
|
@ -44,7 +44,7 @@ use incremental::{REFLOW, REFLOW_OUT_OF_FLOW};
|
||||||
use layout_debug;
|
use layout_debug;
|
||||||
use model::{IntrinsicISizes, MarginCollapseInfo};
|
use model::{IntrinsicISizes, MarginCollapseInfo};
|
||||||
use model::{MaybeAuto, CollapsibleMargins, specified, specified_or_none};
|
use model::{MaybeAuto, CollapsibleMargins, specified, specified_or_none};
|
||||||
use table::ColumnComputedInlineSize;
|
use table;
|
||||||
use wrapper::ThreadSafeLayoutNode;
|
use wrapper::ThreadSafeLayoutNode;
|
||||||
|
|
||||||
use geom::{Point2D, Rect, Size2D};
|
use geom::{Point2D, Rect, Size2D};
|
||||||
|
@ -53,13 +53,13 @@ use msg::compositor_msg::LayerId;
|
||||||
use rustc_serialize::{Encoder, Encodable};
|
use rustc_serialize::{Encoder, Encodable};
|
||||||
use std::cmp::{max, min};
|
use std::cmp::{max, min};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::sync::Arc;
|
||||||
use style::computed_values::{overflow_x, overflow_y, position, box_sizing, display, float};
|
use style::computed_values::{overflow_x, overflow_y, position, box_sizing, display, float};
|
||||||
use style::properties::ComputedValues;
|
use style::properties::ComputedValues;
|
||||||
use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};
|
use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};
|
||||||
use style::values::computed::{LengthOrPercentageOrNone};
|
use style::values::computed::{LengthOrPercentageOrNone};
|
||||||
use std::sync::Arc;
|
|
||||||
use util::geometry::{Au, MAX_AU};
|
use util::geometry::{Au, MAX_AU};
|
||||||
use util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize, WritingMode};
|
use util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize};
|
||||||
use util::opts;
|
use util::opts;
|
||||||
|
|
||||||
/// Information specific to floated blocks.
|
/// Information specific to floated blocks.
|
||||||
|
@ -302,11 +302,11 @@ impl BSizeConstraintSolution {
|
||||||
/// current calculated value of `height`.
|
/// current calculated value of `height`.
|
||||||
///
|
///
|
||||||
/// See CSS 2.1 § 10.7.
|
/// See CSS 2.1 § 10.7.
|
||||||
struct CandidateBSizeIterator {
|
pub struct CandidateBSizeIterator {
|
||||||
block_size: MaybeAuto,
|
block_size: MaybeAuto,
|
||||||
max_block_size: Option<Au>,
|
max_block_size: Option<Au>,
|
||||||
min_block_size: Au,
|
min_block_size: Au,
|
||||||
candidate_value: Au,
|
pub candidate_value: Au,
|
||||||
status: CandidateBSizeIteratorStatus,
|
status: CandidateBSizeIteratorStatus,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -510,7 +510,7 @@ enum FormattingContextType {
|
||||||
//
|
//
|
||||||
// TODO(#1244, #2007, pcwalton): Do this for CSS transforms and opacity too, at least if they're
|
// TODO(#1244, #2007, pcwalton): Do this for CSS transforms and opacity too, at least if they're
|
||||||
// animating.
|
// animating.
|
||||||
fn propagate_layer_flag_from_child(layers_needed_for_descendants: &mut bool, kid: &mut Flow) {
|
pub fn propagate_layer_flag_from_child(layers_needed_for_descendants: &mut bool, kid: &mut Flow) {
|
||||||
if kid.is_absolute_containing_block() {
|
if kid.is_absolute_containing_block() {
|
||||||
let kid_base = flow::mut_base(kid);
|
let kid_base = flow::mut_base(kid);
|
||||||
if kid_base.flags.contains(NEEDS_LAYER) {
|
if kid_base.flags.contains(NEEDS_LAYER) {
|
||||||
|
@ -1234,7 +1234,7 @@ impl BlockFlow {
|
||||||
layout_context: &LayoutContext,
|
layout_context: &LayoutContext,
|
||||||
inline_start_content_edge: Au,
|
inline_start_content_edge: Au,
|
||||||
content_inline_size: Au,
|
content_inline_size: Au,
|
||||||
optional_column_computed_inline_sizes: Option<&[ColumnComputedInlineSize]>) {
|
table_info: Option<table::ChildInlineSizeInfo>) {
|
||||||
// Keep track of whether floats could impact each child.
|
// Keep track of whether floats could impact each child.
|
||||||
let mut inline_start_floats_impact_child =
|
let mut inline_start_floats_impact_child =
|
||||||
self.base.flags.contains(IMPACTED_BY_LEFT_FLOATS);
|
self.base.flags.contains(IMPACTED_BY_LEFT_FLOATS);
|
||||||
|
@ -1256,9 +1256,6 @@ impl BlockFlow {
|
||||||
let fixed_static_i_offset = self.base.fixed_static_i_offset + inline_start_content_edge;
|
let fixed_static_i_offset = self.base.fixed_static_i_offset + inline_start_content_edge;
|
||||||
let flags = self.base.flags.clone();
|
let flags = self.base.flags.clone();
|
||||||
|
|
||||||
// This value is used only for table cells.
|
|
||||||
let mut inline_start_margin_edge = inline_start_content_edge;
|
|
||||||
|
|
||||||
// Remember the inline-sizes of the last left and right floats, if there were any. These
|
// Remember the inline-sizes of the last left and right floats, if there were any. These
|
||||||
// are used for estimating the inline-sizes of block formatting contexts. (We estimate that
|
// are used for estimating the inline-sizes of block formatting contexts. (We estimate that
|
||||||
// the inline-size of any block formatting context that we see will be based on the
|
// the inline-size of any block formatting context that we see will be based on the
|
||||||
|
@ -1286,7 +1283,8 @@ impl BlockFlow {
|
||||||
(LengthOrPercentageOrAuto::Percentage(percent), Some(container_size)) => {
|
(LengthOrPercentageOrAuto::Percentage(percent), Some(container_size)) => {
|
||||||
Some(container_size.scale_by(percent))
|
Some(container_size.scale_by(percent))
|
||||||
}
|
}
|
||||||
(LengthOrPercentageOrAuto::Percentage(_), None) | (LengthOrPercentageOrAuto::Auto, _) => None,
|
(LengthOrPercentageOrAuto::Percentage(_), None) |
|
||||||
|
(LengthOrPercentageOrAuto::Auto, _) => None,
|
||||||
(LengthOrPercentageOrAuto::Length(length), _) => Some(length),
|
(LengthOrPercentageOrAuto::Length(length), _) => Some(length),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1299,6 +1297,13 @@ impl BlockFlow {
|
||||||
// FIXME (mbrubeck): Get correct mode for absolute containing block
|
// FIXME (mbrubeck): Get correct mode for absolute containing block
|
||||||
let containing_block_mode = self.base.writing_mode;
|
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)
|
||||||
|
};
|
||||||
|
|
||||||
for (i, kid) in self.base.child_iter().enumerate() {
|
for (i, kid) in self.base.child_iter().enumerate() {
|
||||||
{
|
{
|
||||||
let kid_base = flow::mut_base(kid);
|
let kid_base = flow::mut_base(kid);
|
||||||
|
@ -1376,16 +1381,12 @@ impl BlockFlow {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle tables.
|
// Handle tables.
|
||||||
match optional_column_computed_inline_sizes {
|
if let Some(ref table_info) = table_info {
|
||||||
Some(ref column_computed_inline_sizes) => {
|
table_info.propagate_to_child(kid,
|
||||||
propagate_column_inline_sizes_to_child(kid,
|
i,
|
||||||
i,
|
content_inline_size,
|
||||||
content_inline_size,
|
containing_block_mode,
|
||||||
containing_block_mode,
|
&mut inline_start_margin_edge);
|
||||||
*column_computed_inline_sizes,
|
|
||||||
&mut inline_start_margin_edge)
|
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Per CSS 2.1 § 16.3.1, text alignment propagates to all children in flow.
|
// Per CSS 2.1 § 16.3.1, text alignment propagates to all children in flow.
|
||||||
|
@ -2661,38 +2662,3 @@ impl ISizeAndMarginsComputer for FloatReplaced {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn propagate_column_inline_sizes_to_child(
|
|
||||||
kid: &mut Flow,
|
|
||||||
child_index: uint,
|
|
||||||
content_inline_size: Au,
|
|
||||||
writing_mode: WritingMode,
|
|
||||||
column_computed_inline_sizes: &[ColumnComputedInlineSize],
|
|
||||||
inline_start_margin_edge: &mut Au) {
|
|
||||||
// If kid is table_rowgroup or table_row, the column inline-sizes info should be copied from
|
|
||||||
// its parent.
|
|
||||||
//
|
|
||||||
// FIXME(pcwalton): This seems inefficient. Reference count it instead?
|
|
||||||
let inline_size = if kid.is_table() || kid.is_table_rowgroup() || kid.is_table_row() {
|
|
||||||
*kid.column_computed_inline_sizes() =
|
|
||||||
column_computed_inline_sizes.iter().map(|&x| x).collect();
|
|
||||||
|
|
||||||
// ISize of kid flow is our content inline-size.
|
|
||||||
content_inline_size
|
|
||||||
} else if kid.is_table_cell() {
|
|
||||||
column_computed_inline_sizes[child_index].size
|
|
||||||
} else {
|
|
||||||
// ISize of kid flow is our content inline-size.
|
|
||||||
content_inline_size
|
|
||||||
};
|
|
||||||
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if kid.is_table_cell() {
|
|
||||||
*inline_start_margin_edge = *inline_start_margin_edge + inline_size
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -886,7 +886,8 @@ impl Fragment {
|
||||||
SpecificFragmentInfo::InlineBlock(_) => {
|
SpecificFragmentInfo::InlineBlock(_) => {
|
||||||
QuantitiesIncludedInIntrinsicInlineSizes::all()
|
QuantitiesIncludedInIntrinsicInlineSizes::all()
|
||||||
}
|
}
|
||||||
SpecificFragmentInfo::Table | SpecificFragmentInfo::TableCell => {
|
SpecificFragmentInfo::Table |
|
||||||
|
SpecificFragmentInfo::TableCell => {
|
||||||
INTRINSIC_INLINE_SIZE_INCLUDES_PADDING |
|
INTRINSIC_INLINE_SIZE_INCLUDES_PADDING |
|
||||||
INTRINSIC_INLINE_SIZE_INCLUDES_BORDER |
|
INTRINSIC_INLINE_SIZE_INCLUDES_BORDER |
|
||||||
INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED
|
INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED
|
||||||
|
|
|
@ -6,29 +6,30 @@
|
||||||
|
|
||||||
#![deny(unsafe_blocks)]
|
#![deny(unsafe_blocks)]
|
||||||
|
|
||||||
use block::{BlockFlow, ISizeAndMarginsComputer, MarginsMayCollapseFlag};
|
use block::{self, BlockFlow, CandidateBSizeIterator, ISizeAndMarginsComputer};
|
||||||
use block::{ISizeConstraintInput, ISizeConstraintSolution};
|
use block::{ISizeConstraintInput, ISizeConstraintSolution};
|
||||||
use context::LayoutContext;
|
use context::LayoutContext;
|
||||||
use floats::FloatKind;
|
use floats::FloatKind;
|
||||||
use flow::{self, Flow, FlowClass, IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS};
|
use flow::{self, Flow, FlowClass, IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS};
|
||||||
use flow::ImmutableFlowUtils;
|
use flow::{ImmutableFlowUtils, MutableFlowUtils};
|
||||||
use fragment::{Fragment, FragmentBorderBoxIterator};
|
use fragment::{Fragment, FragmentBorderBoxIterator};
|
||||||
|
use incremental::{REFLOW, REFLOW_OUT_OF_FLOW};
|
||||||
use layout_debug;
|
use layout_debug;
|
||||||
use model::{IntrinsicISizes, IntrinsicISizesContribution};
|
use model::{IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto};
|
||||||
use table_row::CellIntrinsicInlineSize;
|
use table_row::CellIntrinsicInlineSize;
|
||||||
use table_wrapper::TableLayout;
|
use table_wrapper::TableLayout;
|
||||||
use wrapper::ThreadSafeLayoutNode;
|
use wrapper::ThreadSafeLayoutNode;
|
||||||
|
|
||||||
use geom::{Point2D, Rect};
|
use geom::{Point2D, Rect};
|
||||||
use util::geometry::Au;
|
|
||||||
use util::logical_geometry::LogicalRect;
|
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use style::computed_values::{border_collapse, border_spacing, table_layout};
|
||||||
use style::properties::ComputedValues;
|
use style::properties::ComputedValues;
|
||||||
use style::values::CSSFloat;
|
use style::values::CSSFloat;
|
||||||
use style::values::computed::{LengthOrPercentageOrAuto};
|
use style::values::computed::LengthOrPercentageOrAuto;
|
||||||
use style::computed_values::table_layout;
|
use util::geometry::Au;
|
||||||
use std::sync::Arc;
|
use util::logical_geometry::{LogicalRect, WritingMode};
|
||||||
|
|
||||||
/// A table flow corresponded to the table's internal table fragment under a table wrapper flow.
|
/// 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,
|
/// The properties `position`, `float`, and `margin-*` are used on the table wrapper fragment,
|
||||||
|
@ -140,17 +141,6 @@ impl TableFlow {
|
||||||
total_inline_sizes
|
total_inline_sizes
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assign block-size for table flow.
|
|
||||||
///
|
|
||||||
/// TODO(#2014, pcwalton): This probably doesn't handle margin collapse right.
|
|
||||||
///
|
|
||||||
/// inline(always) because this is only ever called by in-order or non-in-order top-level
|
|
||||||
/// methods
|
|
||||||
#[inline(always)]
|
|
||||||
fn assign_block_size_table_base<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
|
|
||||||
self.block_flow.assign_block_size_block_base(layout_context, MarginsMayCollapseFlag::MarginsMayNotCollapse);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Updates the minimum and preferred inline-size calculation for a single row. This is
|
/// Updates the minimum and preferred inline-size calculation for a single row. This is
|
||||||
/// factored out into a separate function because we process children of rowgroups too.
|
/// factored out into a separate function because we process children of rowgroups too.
|
||||||
fn update_column_inline_sizes_for_row(child: &mut Flow,
|
fn update_column_inline_sizes_for_row(child: &mut Flow,
|
||||||
|
@ -183,6 +173,20 @@ impl TableFlow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the effective spacing per cell, taking the value of `border-collapse` into account.
|
||||||
|
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,
|
||||||
|
border_collapse::T::collapse => {
|
||||||
|
border_spacing::T {
|
||||||
|
horizontal: Au(0),
|
||||||
|
vertical: Au(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Flow for TableFlow {
|
impl Flow for TableFlow {
|
||||||
|
@ -218,6 +222,8 @@ impl Flow for TableFlow {
|
||||||
let _scope = layout_debug_scope!("table::bubble_inline_sizes {:x}",
|
let _scope = layout_debug_scope!("table::bubble_inline_sizes {:x}",
|
||||||
self.block_flow.base.debug_id());
|
self.block_flow.base.debug_id());
|
||||||
|
|
||||||
|
// 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.
|
||||||
let mut computation = IntrinsicISizesContribution::new();
|
let mut computation = IntrinsicISizesContribution::new();
|
||||||
let mut did_first_row = false;
|
let mut did_first_row = false;
|
||||||
for kid in self.block_flow.base.child_iter() {
|
for kid in self.block_flow.base.child_iter() {
|
||||||
|
@ -256,6 +262,14 @@ impl Flow for TableFlow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let spacing = self.block_flow
|
||||||
|
.fragment
|
||||||
|
.style()
|
||||||
|
.get_inheritedtable()
|
||||||
|
.border_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()
|
self.block_flow.base.intrinsic_inline_sizes = computation.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,24 +286,26 @@ impl Flow for TableFlow {
|
||||||
let mut num_unspecified_inline_sizes = 0;
|
let mut num_unspecified_inline_sizes = 0;
|
||||||
let mut total_column_inline_size = Au(0);
|
let mut total_column_inline_size = Au(0);
|
||||||
for column_inline_size in self.column_intrinsic_inline_sizes.iter() {
|
for column_inline_size in self.column_intrinsic_inline_sizes.iter() {
|
||||||
let this_column_inline_size = column_inline_size.minimum_length;
|
if column_inline_size.constrained {
|
||||||
if this_column_inline_size == Au(0) {
|
total_column_inline_size = total_column_inline_size +
|
||||||
num_unspecified_inline_sizes += 1
|
column_inline_size.minimum_length
|
||||||
} else {
|
} else {
|
||||||
total_column_inline_size = total_column_inline_size + this_column_inline_size
|
num_unspecified_inline_sizes += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let inline_size_computer = InternalTable;
|
let inline_size_computer = InternalTable;
|
||||||
|
|
||||||
inline_size_computer.compute_used_inline_size(&mut self.block_flow,
|
inline_size_computer.compute_used_inline_size(&mut self.block_flow,
|
||||||
layout_context,
|
layout_context,
|
||||||
containing_block_inline_size);
|
containing_block_inline_size);
|
||||||
|
|
||||||
let inline_start_content_edge = self.block_flow.fragment.border_padding.inline_start;
|
let inline_start_content_edge = self.block_flow.fragment.border_padding.inline_start;
|
||||||
let padding_and_borders = self.block_flow.fragment.border_padding.inline_start_end();
|
let padding_and_borders = self.block_flow.fragment.border_padding.inline_start_end();
|
||||||
|
let spacing_per_cell = self.spacing();
|
||||||
|
let spacing = spacing_per_cell.horizontal *
|
||||||
|
(self.column_intrinsic_inline_sizes.len() as i32 + 1);
|
||||||
let content_inline_size =
|
let content_inline_size =
|
||||||
self.block_flow.fragment.border_box.size.inline - padding_and_borders;
|
self.block_flow.fragment.border_box.size.inline - padding_and_borders - spacing;
|
||||||
|
|
||||||
match self.table_layout {
|
match self.table_layout {
|
||||||
TableLayout::Fixed => {
|
TableLayout::Fixed => {
|
||||||
|
@ -305,11 +321,9 @@ impl Flow for TableFlow {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if num_unspecified_inline_sizes != 0 {
|
} else if num_unspecified_inline_sizes != 0 {
|
||||||
let extra_column_inline_size =
|
let extra_column_inline_size = content_inline_size - total_column_inline_size;
|
||||||
(content_inline_size - total_column_inline_size) /
|
|
||||||
num_unspecified_inline_sizes;
|
|
||||||
for column_inline_size in self.column_intrinsic_inline_sizes.iter() {
|
for column_inline_size in self.column_intrinsic_inline_sizes.iter() {
|
||||||
if column_inline_size.minimum_length == Au(0) &&
|
if !column_inline_size.constrained &&
|
||||||
column_inline_size.percentage == 0.0 {
|
column_inline_size.percentage == 0.0 {
|
||||||
self.column_computed_inline_sizes.push(ColumnComputedInlineSize {
|
self.column_computed_inline_sizes.push(ColumnComputedInlineSize {
|
||||||
size: extra_column_inline_size / num_unspecified_inline_sizes,
|
size: extra_column_inline_size / num_unspecified_inline_sizes,
|
||||||
|
@ -332,16 +346,20 @@ impl Flow for TableFlow {
|
||||||
self.block_flow.base.flags.remove(IMPACTED_BY_LEFT_FLOATS);
|
self.block_flow.base.flags.remove(IMPACTED_BY_LEFT_FLOATS);
|
||||||
self.block_flow.base.flags.remove(IMPACTED_BY_RIGHT_FLOATS);
|
self.block_flow.base.flags.remove(IMPACTED_BY_RIGHT_FLOATS);
|
||||||
|
|
||||||
self.block_flow.propagate_assigned_inline_size_to_children(
|
let info = ChildInlineSizeInfo {
|
||||||
layout_context,
|
column_computed_inline_sizes: self.column_computed_inline_sizes.as_slice(),
|
||||||
inline_start_content_edge,
|
spacing: spacing_per_cell,
|
||||||
content_inline_size,
|
};
|
||||||
Some(self.column_computed_inline_sizes.as_slice()));
|
self.block_flow.propagate_assigned_inline_size_to_children(layout_context,
|
||||||
|
inline_start_content_edge,
|
||||||
|
content_inline_size,
|
||||||
|
Some(info));
|
||||||
}
|
}
|
||||||
|
|
||||||
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");
|
debug!("assign_block_size: assigning block_size for table");
|
||||||
self.assign_block_size_table_base(ctx);
|
let vertical_spacing = self.spacing().vertical;
|
||||||
|
self.block_flow.assign_block_size_for_table_like_flow(layout_context, vertical_spacing)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_absolute_position(&mut self) {
|
fn compute_absolute_position(&mut self) {
|
||||||
|
@ -417,6 +435,90 @@ 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect various offsets needed by absolutely positioned descendants.
|
||||||
|
(&mut *self as &mut Flow).collect_static_block_offsets_from_children();
|
||||||
|
|
||||||
|
// 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.
|
/// 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
|
/// During table inline-size bubbling, we might need to store both a percentage constraint and a
|
||||||
|
@ -472,8 +574,69 @@ impl ColumnIntrinsicInlineSize {
|
||||||
///
|
///
|
||||||
/// TODO(pcwalton): There will probably be some `border-collapse`-related info in here too
|
/// TODO(pcwalton): There will probably be some `border-collapse`-related info in here too
|
||||||
/// eventually.
|
/// eventually.
|
||||||
#[derive(RustcEncodable, Copy)]
|
#[derive(RustcEncodable, Clone, Copy)]
|
||||||
pub struct ColumnComputedInlineSize {
|
pub struct ColumnComputedInlineSize {
|
||||||
/// The computed size of this inline column.
|
/// The computed size of this inline column.
|
||||||
pub size: Au,
|
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],
|
||||||
|
}
|
||||||
|
|
||||||
|
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: uint,
|
||||||
|
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;
|
||||||
|
} else {
|
||||||
|
// ISize of kid flow is our content inline-size.
|
||||||
|
inline_size = content_inline_size
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,8 @@ use context::LayoutContext;
|
||||||
use flow::{self, FlowClass, Flow, ImmutableFlowUtils};
|
use flow::{self, FlowClass, Flow, ImmutableFlowUtils};
|
||||||
use fragment::{Fragment, FragmentBorderBoxIterator};
|
use fragment::{Fragment, FragmentBorderBoxIterator};
|
||||||
use layout_debug;
|
use layout_debug;
|
||||||
use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, InternalTable};
|
use table::{ChildInlineSizeInfo, ColumnComputedInlineSize, ColumnIntrinsicInlineSize};
|
||||||
|
use table::{InternalTable};
|
||||||
use model::MaybeAuto;
|
use model::MaybeAuto;
|
||||||
use wrapper::ThreadSafeLayoutNode;
|
use wrapper::ThreadSafeLayoutNode;
|
||||||
|
|
||||||
|
@ -21,9 +22,10 @@ use util::geometry::Au;
|
||||||
use util::logical_geometry::LogicalRect;
|
use util::logical_geometry::LogicalRect;
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use style::computed_values::border_spacing;
|
||||||
use style::properties::ComputedValues;
|
use style::properties::ComputedValues;
|
||||||
use style::values::computed::LengthOrPercentageOrAuto;
|
use style::values::computed::LengthOrPercentageOrAuto;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
/// A single row of a table.
|
/// A single row of a table.
|
||||||
#[derive(RustcEncodable)]
|
#[derive(RustcEncodable)]
|
||||||
|
@ -35,6 +37,10 @@ pub struct TableRowFlow {
|
||||||
|
|
||||||
/// Information about the computed inline-sizes of each column.
|
/// Information about the computed inline-sizes of each column.
|
||||||
pub column_computed_inline_sizes: Vec<ColumnComputedInlineSize>,
|
pub column_computed_inline_sizes: Vec<ColumnComputedInlineSize>,
|
||||||
|
|
||||||
|
/// The spacing for this row, propagated down from the table during the inline-size assignment
|
||||||
|
/// phase.
|
||||||
|
pub spacing: border_spacing::T,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Information about the column inline size and span for each cell.
|
/// Information about the column inline size and span for each cell.
|
||||||
|
@ -53,6 +59,10 @@ impl TableRowFlow {
|
||||||
block_flow: BlockFlow::from_node_and_fragment(node, fragment),
|
block_flow: BlockFlow::from_node_and_fragment(node, fragment),
|
||||||
cell_intrinsic_inline_sizes: Vec::new(),
|
cell_intrinsic_inline_sizes: Vec::new(),
|
||||||
column_computed_inline_sizes: Vec::new(),
|
column_computed_inline_sizes: Vec::new(),
|
||||||
|
spacing: border_spacing::T {
|
||||||
|
horizontal: Au(0),
|
||||||
|
vertical: Au(0),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,11 +86,9 @@ impl TableRowFlow {
|
||||||
fn assign_block_size_table_row_base<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
|
fn assign_block_size_table_row_base<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
|
||||||
let (block_start_offset, _, _) = self.initialize_offsets();
|
let (block_start_offset, _, _) = self.initialize_offsets();
|
||||||
|
|
||||||
let /* mut */ cur_y = block_start_offset;
|
|
||||||
|
|
||||||
// Per CSS 2.1 § 17.5.3, find max_y = max(computed `block-size`, minimum block-size of all
|
// Per CSS 2.1 § 17.5.3, find max_y = max(computed `block-size`, minimum block-size of all
|
||||||
// cells).
|
// cells).
|
||||||
let mut max_y = Au(0);
|
let mut max_block_size = Au(0);
|
||||||
let thread_id = self.block_flow.base.thread_id;
|
let thread_id = self.block_flow.base.thread_id;
|
||||||
for kid in self.block_flow.base.child_iter() {
|
for kid in self.block_flow.base.child_iter() {
|
||||||
kid.place_float_if_applicable(layout_context);
|
kid.place_float_if_applicable(layout_context);
|
||||||
|
@ -93,17 +101,18 @@ impl TableRowFlow {
|
||||||
// TODO: Percentage block-size
|
// TODO: Percentage block-size
|
||||||
let child_specified_block_size =
|
let child_specified_block_size =
|
||||||
MaybeAuto::from_style(child_fragment.style().content_block_size(),
|
MaybeAuto::from_style(child_fragment.style().content_block_size(),
|
||||||
Au::new(0)).specified_or_zero();
|
Au(0)).specified_or_zero();
|
||||||
max_y = max(max_y,
|
max_block_size =
|
||||||
child_specified_block_size +
|
max(max_block_size,
|
||||||
child_fragment.border_padding.block_start_end());
|
child_specified_block_size +
|
||||||
|
child_fragment.border_padding.block_start_end());
|
||||||
}
|
}
|
||||||
let child_node = flow::mut_base(kid);
|
let child_node = flow::mut_base(kid);
|
||||||
child_node.position.start.b = cur_y;
|
child_node.position.start.b = block_start_offset;
|
||||||
max_y = max(max_y, child_node.position.size.block);
|
max_block_size = max(max_block_size, child_node.position.size.block);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut block_size = max_y;
|
let mut block_size = max_block_size;
|
||||||
// TODO: Percentage block-size
|
// TODO: Percentage block-size
|
||||||
block_size = match MaybeAuto::from_style(self.block_flow
|
block_size = match MaybeAuto::from_style(self.block_flow
|
||||||
.fragment
|
.fragment
|
||||||
|
@ -113,11 +122,8 @@ impl TableRowFlow {
|
||||||
MaybeAuto::Auto => block_size,
|
MaybeAuto::Auto => block_size,
|
||||||
MaybeAuto::Specified(value) => max(value, block_size)
|
MaybeAuto::Specified(value) => max(value, block_size)
|
||||||
};
|
};
|
||||||
// cur_y = cur_y + block-size;
|
|
||||||
|
|
||||||
// Assign the block-size of own fragment
|
// Assign the block-size of own fragment
|
||||||
//
|
|
||||||
// FIXME(pcwalton): Take `cur_y` into account.
|
|
||||||
let mut position = self.block_flow.fragment.border_box;
|
let mut position = self.block_flow.fragment.border_box;
|
||||||
position.size.block = block_size;
|
position.size.block = block_size;
|
||||||
self.block_flow.fragment.border_box = position;
|
self.block_flow.fragment.border_box = position;
|
||||||
|
@ -273,11 +279,14 @@ impl Flow for TableRowFlow {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push those inline sizes down to the cells.
|
// Push those inline sizes down to the cells.
|
||||||
self.block_flow.propagate_assigned_inline_size_to_children(
|
let info = ChildInlineSizeInfo {
|
||||||
layout_context,
|
column_computed_inline_sizes: computed_inline_size_for_cells.as_slice(),
|
||||||
inline_start_content_edge,
|
spacing: self.spacing,
|
||||||
containing_block_inline_size,
|
};
|
||||||
Some(computed_inline_size_for_cells.as_slice()));
|
self.block_flow.propagate_assigned_inline_size_to_children(layout_context,
|
||||||
|
inline_start_content_edge,
|
||||||
|
containing_block_inline_size,
|
||||||
|
Some(info));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
|
fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
|
||||||
|
|
|
@ -6,12 +6,14 @@
|
||||||
|
|
||||||
#![deny(unsafe_blocks)]
|
#![deny(unsafe_blocks)]
|
||||||
|
|
||||||
use block::{BlockFlow, ISizeAndMarginsComputer, MarginsMayCollapseFlag};
|
use block::{BlockFlow, ISizeAndMarginsComputer};
|
||||||
use context::LayoutContext;
|
use context::LayoutContext;
|
||||||
use flow::{FlowClass, Flow};
|
use flow::{FlowClass, Flow};
|
||||||
use fragment::{Fragment, FragmentBorderBoxIterator};
|
use fragment::{Fragment, FragmentBorderBoxIterator};
|
||||||
use layout_debug;
|
use layout_debug;
|
||||||
use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, InternalTable};
|
use style::computed_values::border_spacing;
|
||||||
|
use table::{ChildInlineSizeInfo, ColumnComputedInlineSize, ColumnIntrinsicInlineSize};
|
||||||
|
use table::{InternalTable, TableLikeFlow};
|
||||||
use wrapper::ThreadSafeLayoutNode;
|
use wrapper::ThreadSafeLayoutNode;
|
||||||
|
|
||||||
use geom::{Point2D, Rect};
|
use geom::{Point2D, Rect};
|
||||||
|
@ -32,6 +34,9 @@ pub struct TableRowGroupFlow {
|
||||||
|
|
||||||
/// Information about the actual inline sizes of each column.
|
/// Information about the actual inline sizes of each column.
|
||||||
pub column_computed_inline_sizes: Vec<ColumnComputedInlineSize>,
|
pub column_computed_inline_sizes: Vec<ColumnComputedInlineSize>,
|
||||||
|
|
||||||
|
/// The spacing for this rowgroup.
|
||||||
|
pub spacing: border_spacing::T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TableRowGroupFlow {
|
impl TableRowGroupFlow {
|
||||||
|
@ -41,21 +46,16 @@ impl TableRowGroupFlow {
|
||||||
block_flow: BlockFlow::from_node_and_fragment(node, fragment),
|
block_flow: BlockFlow::from_node_and_fragment(node, fragment),
|
||||||
column_intrinsic_inline_sizes: Vec::new(),
|
column_intrinsic_inline_sizes: Vec::new(),
|
||||||
column_computed_inline_sizes: Vec::new(),
|
column_computed_inline_sizes: Vec::new(),
|
||||||
|
spacing: border_spacing::T {
|
||||||
|
horizontal: Au(0),
|
||||||
|
vertical: Au(0),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fragment<'a>(&'a mut self) -> &'a Fragment {
|
pub fn fragment<'a>(&'a mut self) -> &'a Fragment {
|
||||||
&self.block_flow.fragment
|
&self.block_flow.fragment
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assign block-size for table-rowgroup flow.
|
|
||||||
///
|
|
||||||
/// inline(always) because this is only ever called by in-order or non-in-order top-level
|
|
||||||
/// methods.
|
|
||||||
#[inline(always)]
|
|
||||||
fn assign_block_size_table_rowgroup_base<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
|
|
||||||
self.block_flow.assign_block_size_block_base(layout_context, MarginsMayCollapseFlag::MarginsMayNotCollapse)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Flow for TableRowGroupFlow {
|
impl Flow for TableRowGroupFlow {
|
||||||
|
@ -109,16 +109,20 @@ impl Flow for TableRowGroupFlow {
|
||||||
layout_context,
|
layout_context,
|
||||||
containing_block_inline_size);
|
containing_block_inline_size);
|
||||||
|
|
||||||
self.block_flow.propagate_assigned_inline_size_to_children(
|
let info = ChildInlineSizeInfo {
|
||||||
layout_context,
|
column_computed_inline_sizes: self.column_computed_inline_sizes.as_slice(),
|
||||||
inline_start_content_edge,
|
spacing: self.spacing,
|
||||||
content_inline_size,
|
};
|
||||||
Some(self.column_computed_inline_sizes.as_slice()));
|
self.block_flow.propagate_assigned_inline_size_to_children(layout_context,
|
||||||
|
inline_start_content_edge,
|
||||||
|
content_inline_size,
|
||||||
|
Some(info));
|
||||||
}
|
}
|
||||||
|
|
||||||
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_rowgroup");
|
debug!("assign_block_size: assigning block_size for table_rowgroup");
|
||||||
self.assign_block_size_table_rowgroup_base(ctx);
|
self.block_flow.assign_block_size_for_table_like_flow(layout_context,
|
||||||
|
self.spacing.vertical)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_absolute_position(&mut self) {
|
fn compute_absolute_position(&mut self) {
|
||||||
|
|
|
@ -20,7 +20,7 @@ use floats::FloatKind;
|
||||||
use flow::{FlowClass, Flow, ImmutableFlowUtils};
|
use flow::{FlowClass, Flow, ImmutableFlowUtils};
|
||||||
use flow::{IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS};
|
use flow::{IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS};
|
||||||
use fragment::{Fragment, FragmentBorderBoxIterator};
|
use fragment::{Fragment, FragmentBorderBoxIterator};
|
||||||
use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize};
|
use table::{ChildInlineSizeInfo, ColumnComputedInlineSize, ColumnIntrinsicInlineSize};
|
||||||
use wrapper::ThreadSafeLayoutNode;
|
use wrapper::ThreadSafeLayoutNode;
|
||||||
|
|
||||||
use geom::{Point2D, Rect};
|
use geom::{Point2D, Rect};
|
||||||
|
@ -28,11 +28,11 @@ use util::geometry::Au;
|
||||||
use std::cmp::{max, min};
|
use std::cmp::{max, min};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops::Add;
|
use std::ops::Add;
|
||||||
use style::properties::ComputedValues;
|
use std::sync::Arc;
|
||||||
use style::computed_values::table_layout;
|
use style::computed_values::table_layout;
|
||||||
|
use style::properties::ComputedValues;
|
||||||
use style::values::CSSFloat;
|
use style::values::CSSFloat;
|
||||||
use style::values::computed::LengthOrPercentageOrAuto;
|
use style::values::computed::LengthOrPercentageOrAuto;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
#[derive(Copy, RustcEncodable, Debug)]
|
#[derive(Copy, RustcEncodable, Debug)]
|
||||||
pub enum TableLayout {
|
pub enum TableLayout {
|
||||||
|
@ -98,11 +98,19 @@ impl TableWrapperFlow {
|
||||||
// when normally the child computes it itself. But it has to be this way because the
|
// 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
|
// 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.
|
// tables are separated into table flows and table wrapper flows.
|
||||||
let available_inline_size = self.block_flow.fragment.border_box.size.inline;
|
let mut available_inline_size = self.block_flow.fragment.border_box.size.inline;
|
||||||
let mut table_border_padding = Au(0);
|
let (mut table_border_padding, mut spacing) = (Au(0), Au(0));
|
||||||
for kid in self.block_flow.base.child_iter() {
|
for kid in self.block_flow.base.child_iter() {
|
||||||
if kid.is_table() {
|
if kid.is_table() {
|
||||||
let kid_block = kid.as_block();
|
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_border_and_padding(available_inline_size);
|
||||||
kid_block.fragment.compute_block_direction_margins(available_inline_size);
|
kid_block.fragment.compute_block_direction_margins(available_inline_size);
|
||||||
kid_block.fragment.compute_inline_direction_margins(available_inline_size);
|
kid_block.fragment.compute_inline_direction_margins(available_inline_size);
|
||||||
|
@ -114,18 +122,21 @@ impl TableWrapperFlow {
|
||||||
// FIXME(pcwalton, spec): INTRINSIC § 8 does not properly define how to compute this, but
|
// FIXME(pcwalton, spec): INTRINSIC § 8 does not properly define how to compute this, but
|
||||||
// says "the basic idea is the same as the shrink-to-fit width that CSS2.1 defines". So we
|
// says "the basic idea is the same as the shrink-to-fit width that CSS2.1 defines". So we
|
||||||
// just use the shrink-to-fit inline size.
|
// just use the shrink-to-fit inline size.
|
||||||
let available_inline_size = match self.block_flow.fragment.style().content_inline_size() {
|
let mut available_inline_size =
|
||||||
LengthOrPercentageOrAuto::Auto => self.block_flow
|
match self.block_flow.fragment.style().content_inline_size() {
|
||||||
.get_shrink_to_fit_inline_size(available_inline_size),
|
LengthOrPercentageOrAuto::Auto => {
|
||||||
// FIXME(mttr) This fixes #4421 without breaking our current reftests, but I'm
|
self.block_flow.get_shrink_to_fit_inline_size(available_inline_size)
|
||||||
// not completely sure this is "correct".
|
}
|
||||||
//
|
// FIXME(mttr): This fixes #4421 without breaking our current reftests, but I'm
|
||||||
// That said, `available_inline_size` is, as far as I can tell, equal to the table's
|
// not completely sure this is "correct".
|
||||||
// computed width property (W) and is used from this point forward in a way that seems
|
//
|
||||||
// to correspond with CSS 2.1 § 17.5.2.2 under "Column and caption widths
|
// That said, `available_inline_size` is, as far as I can tell, equal to the
|
||||||
// influence the final table width as follows: ..."
|
// table's computed width property (W) and is used from this point forward in a way
|
||||||
_ => available_inline_size,
|
// that seems to correspond with CSS 2.1 § 17.5.2.2 under "Column and caption
|
||||||
};
|
// widths influence the final table width as follows: …"
|
||||||
|
_ => available_inline_size,
|
||||||
|
};
|
||||||
|
available_inline_size = available_inline_size - spacing;
|
||||||
|
|
||||||
// Compute all the guesses for the column sizes, and sum them.
|
// Compute all the guesses for the column sizes, and sum them.
|
||||||
let mut total_guess = AutoLayoutCandidateGuess::new();
|
let mut total_guess = AutoLayoutCandidateGuess::new();
|
||||||
|
@ -153,8 +164,8 @@ impl TableWrapperFlow {
|
||||||
//
|
//
|
||||||
// FIXME(pcwalton, spec): How do I deal with fractional excess?
|
// FIXME(pcwalton, spec): How do I deal with fractional excess?
|
||||||
let excess_inline_size = available_inline_size - total_used_inline_size;
|
let excess_inline_size = available_inline_size - total_used_inline_size;
|
||||||
if excess_inline_size > Au(0) &&
|
if excess_inline_size > Au(0) && selection ==
|
||||||
selection == SelectedAutoLayoutCandidateGuess::UsePreferredGuessAndDistributeExcessInlineSize {
|
SelectedAutoLayoutCandidateGuess::UsePreferredGuessAndDistributeExcessInlineSize {
|
||||||
let mut info = ExcessInlineSizeDistributionInfo::new();
|
let mut info = ExcessInlineSizeDistributionInfo::new();
|
||||||
for column_intrinsic_inline_size in self.column_intrinsic_inline_sizes.iter() {
|
for column_intrinsic_inline_size in self.column_intrinsic_inline_sizes.iter() {
|
||||||
info.update(column_intrinsic_inline_size)
|
info.update(column_intrinsic_inline_size)
|
||||||
|
@ -173,10 +184,12 @@ impl TableWrapperFlow {
|
||||||
total_used_inline_size = available_inline_size
|
total_used_inline_size = available_inline_size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
self.block_flow.fragment.border_box.size.inline = total_used_inline_size +
|
self.block_flow.fragment.border_box.size.inline = total_used_inline_size +
|
||||||
table_border_padding;
|
table_border_padding + spacing;
|
||||||
self.block_flow.base.position.size.inline = total_used_inline_size +
|
self.block_flow.base.position.size.inline = total_used_inline_size +
|
||||||
table_border_padding + self.block_flow.fragment.margin.inline_start_end();
|
table_border_padding + spacing + self.block_flow.fragment.margin.inline_start_end();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_used_inline_size(&mut self,
|
fn compute_used_inline_size(&mut self,
|
||||||
|
@ -301,11 +314,15 @@ impl Flow for TableWrapperFlow {
|
||||||
None)
|
None)
|
||||||
}
|
}
|
||||||
Some(ref assigned_column_inline_sizes) => {
|
Some(ref assigned_column_inline_sizes) => {
|
||||||
self.block_flow.propagate_assigned_inline_size_to_children(
|
let info = ChildInlineSizeInfo {
|
||||||
layout_context,
|
column_computed_inline_sizes: assigned_column_inline_sizes.as_slice(),
|
||||||
inline_start_content_edge,
|
spacing: self.block_flow.fragment.style().get_inheritedtable().border_spacing,
|
||||||
content_inline_size,
|
};
|
||||||
Some(assigned_column_inline_sizes.as_slice()));
|
self.block_flow
|
||||||
|
.propagate_assigned_inline_size_to_children(layout_context,
|
||||||
|
inline_start_content_edge,
|
||||||
|
content_inline_size,
|
||||||
|
Some(info));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,13 +475,16 @@ impl AutoLayoutCandidateGuess {
|
||||||
fn calculate(&self, selection: SelectedAutoLayoutCandidateGuess) -> Au {
|
fn calculate(&self, selection: SelectedAutoLayoutCandidateGuess) -> Au {
|
||||||
match selection {
|
match selection {
|
||||||
SelectedAutoLayoutCandidateGuess::UseMinimumGuess => self.minimum_guess,
|
SelectedAutoLayoutCandidateGuess::UseMinimumGuess => self.minimum_guess,
|
||||||
SelectedAutoLayoutCandidateGuess::InterpolateBetweenMinimumGuessAndMinimumPercentageGuess(weight) => {
|
SelectedAutoLayoutCandidateGuess::
|
||||||
|
InterpolateBetweenMinimumGuessAndMinimumPercentageGuess(weight) => {
|
||||||
interp(self.minimum_guess, self.minimum_percentage_guess, weight)
|
interp(self.minimum_guess, self.minimum_percentage_guess, weight)
|
||||||
}
|
}
|
||||||
SelectedAutoLayoutCandidateGuess::InterpolateBetweenMinimumPercentageGuessAndMinimumSpecifiedGuess(weight) => {
|
SelectedAutoLayoutCandidateGuess::
|
||||||
|
InterpolateBetweenMinimumPercentageGuessAndMinimumSpecifiedGuess(weight) => {
|
||||||
interp(self.minimum_percentage_guess, self.minimum_specified_guess, weight)
|
interp(self.minimum_percentage_guess, self.minimum_specified_guess, weight)
|
||||||
}
|
}
|
||||||
SelectedAutoLayoutCandidateGuess::InterpolateBetweenMinimumSpecifiedGuessAndPreferredGuess(weight) => {
|
SelectedAutoLayoutCandidateGuess::
|
||||||
|
InterpolateBetweenMinimumSpecifiedGuessAndPreferredGuess(weight) => {
|
||||||
interp(self.minimum_specified_guess, self.preferred_guess, weight)
|
interp(self.minimum_specified_guess, self.preferred_guess, weight)
|
||||||
}
|
}
|
||||||
SelectedAutoLayoutCandidateGuess::UsePreferredGuessAndDistributeExcessInlineSize => {
|
SelectedAutoLayoutCandidateGuess::UsePreferredGuessAndDistributeExcessInlineSize => {
|
||||||
|
@ -590,9 +610,17 @@ impl ExcessInlineSizeDistributionInfo {
|
||||||
total_distributed_excess_size: &mut Au) {
|
total_distributed_excess_size: &mut Au) {
|
||||||
let proportion =
|
let proportion =
|
||||||
if self.preferred_inline_size_of_nonconstrained_columns_with_no_percentage > Au(0) {
|
if self.preferred_inline_size_of_nonconstrained_columns_with_no_percentage > Au(0) {
|
||||||
column_intrinsic_inline_size.preferred.to_subpx() /
|
// FIXME(spec, pcwalton): Gecko and WebKit do *something* here when there are
|
||||||
self.preferred_inline_size_of_nonconstrained_columns_with_no_percentage
|
// nonconstrained columns with no percentage *and* no preferred width. What do they
|
||||||
.to_subpx()
|
// do?
|
||||||
|
if !column_intrinsic_inline_size.constrained &&
|
||||||
|
column_intrinsic_inline_size.percentage == 0.0 {
|
||||||
|
column_intrinsic_inline_size.preferred.to_subpx() /
|
||||||
|
self.preferred_inline_size_of_nonconstrained_columns_with_no_percentage
|
||||||
|
.to_subpx()
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
} else if self.count_of_nonconstrained_columns_with_no_percentage > 0 {
|
} else if self.count_of_nonconstrained_columns_with_no_percentage > 0 {
|
||||||
1.0 / (self.count_of_nonconstrained_columns_with_no_percentage as CSSFloat)
|
1.0 / (self.count_of_nonconstrained_columns_with_no_percentage as CSSFloat)
|
||||||
} else if self.preferred_inline_size_of_constrained_columns_with_no_percentage >
|
} else if self.preferred_inline_size_of_constrained_columns_with_no_percentage >
|
||||||
|
|
|
@ -635,8 +635,8 @@ impl<'le> TElement<'le> for LayoutElement<'le> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn has_nonzero_border(self) -> bool {
|
fn has_nonzero_border(self) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
match self.element
|
match self.element.get_unsigned_integer_attribute_for_layout(
|
||||||
.get_unsigned_integer_attribute_for_layout(UnsignedIntegerAttribute::Border) {
|
UnsignedIntegerAttribute::Border) {
|
||||||
None | Some(0) => false,
|
None | Some(0) => false,
|
||||||
_ => true,
|
_ => true,
|
||||||
}
|
}
|
||||||
|
|
|
@ -317,6 +317,16 @@ impl RawLayoutElementHelpers for Element {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
UnsignedIntegerAttribute::CellSpacing => {
|
||||||
|
if self.is_htmltableelement() {
|
||||||
|
let this: &HTMLTableElement = mem::transmute(self);
|
||||||
|
this.get_cellspacing()
|
||||||
|
} else {
|
||||||
|
// Don't panic since `display` can cause this to be called on arbitrary
|
||||||
|
// elements.
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
UnsignedIntegerAttribute::ColSpan => {
|
UnsignedIntegerAttribute::ColSpan => {
|
||||||
if self.is_htmltablecellelement() {
|
if self.is_htmltablecellelement() {
|
||||||
let this: &HTMLTableCellElement = mem::transmute(self);
|
let this: &HTMLTableCellElement = mem::transmute(self);
|
||||||
|
|
|
@ -26,6 +26,7 @@ pub struct HTMLTableElement {
|
||||||
htmlelement: HTMLElement,
|
htmlelement: HTMLElement,
|
||||||
background_color: Cell<Option<RGBA>>,
|
background_color: Cell<Option<RGBA>>,
|
||||||
border: Cell<Option<u32>>,
|
border: Cell<Option<u32>>,
|
||||||
|
cellspacing: Cell<Option<u32>>,
|
||||||
width: Cell<LengthOrPercentageOrAuto>,
|
width: Cell<LengthOrPercentageOrAuto>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,6 +46,7 @@ impl HTMLTableElement {
|
||||||
document),
|
document),
|
||||||
background_color: Cell::new(None),
|
background_color: Cell::new(None),
|
||||||
border: Cell::new(None),
|
border: Cell::new(None),
|
||||||
|
cellspacing: Cell::new(None),
|
||||||
width: Cell::new(LengthOrPercentageOrAuto::Auto),
|
width: Cell::new(LengthOrPercentageOrAuto::Auto),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,6 +96,7 @@ impl<'a> HTMLTableElementMethods for JSRef<'a, HTMLTableElement> {
|
||||||
pub trait HTMLTableElementHelpers {
|
pub trait HTMLTableElementHelpers {
|
||||||
fn get_background_color(&self) -> Option<RGBA>;
|
fn get_background_color(&self) -> Option<RGBA>;
|
||||||
fn get_border(&self) -> Option<u32>;
|
fn get_border(&self) -> Option<u32>;
|
||||||
|
fn get_cellspacing(&self) -> Option<u32>;
|
||||||
fn get_width(&self) -> LengthOrPercentageOrAuto;
|
fn get_width(&self) -> LengthOrPercentageOrAuto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,6 +109,10 @@ impl HTMLTableElementHelpers for HTMLTableElement {
|
||||||
self.border.get()
|
self.border.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_cellspacing(&self) -> Option<u32> {
|
||||||
|
self.cellspacing.get()
|
||||||
|
}
|
||||||
|
|
||||||
fn get_width(&self) -> LengthOrPercentageOrAuto {
|
fn get_width(&self) -> LengthOrPercentageOrAuto {
|
||||||
self.width.get()
|
self.width.get()
|
||||||
}
|
}
|
||||||
|
@ -132,6 +139,9 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLTableElement> {
|
||||||
.as_slice()
|
.as_slice()
|
||||||
.chars()).unwrap_or(1)))
|
.chars()).unwrap_or(1)))
|
||||||
}
|
}
|
||||||
|
&atom!("cellspacing") => {
|
||||||
|
self.cellspacing.set(str::parse_unsigned_integer(attr.value().as_slice().chars()))
|
||||||
|
}
|
||||||
&atom!("width") => self.width.set(str::parse_length(attr.value().as_slice())),
|
&atom!("width") => self.width.set(str::parse_length(attr.value().as_slice())),
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
|
@ -145,6 +155,7 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLTableElement> {
|
||||||
match attr.local_name() {
|
match attr.local_name() {
|
||||||
&atom!("bgcolor") => self.background_color.set(None),
|
&atom!("bgcolor") => self.background_color.set(None),
|
||||||
&atom!("border") => self.border.set(None),
|
&atom!("border") => self.border.set(None),
|
||||||
|
&atom!("cellspacing") => self.cellspacing.set(None),
|
||||||
&atom!("width") => self.width.set(LengthOrPercentageOrAuto::Auto),
|
&atom!("width") => self.width.set(LengthOrPercentageOrAuto::Auto),
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,8 +41,10 @@ partial interface CSSStyleDeclaration {
|
||||||
[TreatNullAs=EmptyString] attribute DOMString backgroundSize;
|
[TreatNullAs=EmptyString] attribute DOMString backgroundSize;
|
||||||
|
|
||||||
[TreatNullAs=EmptyString] attribute DOMString border;
|
[TreatNullAs=EmptyString] attribute DOMString border;
|
||||||
|
[TreatNullAs=EmptyString] attribute DOMString borderCollapse;
|
||||||
[TreatNullAs=EmptyString] attribute DOMString borderColor;
|
[TreatNullAs=EmptyString] attribute DOMString borderColor;
|
||||||
[TreatNullAs=EmptyString] attribute DOMString borderRadius;
|
[TreatNullAs=EmptyString] attribute DOMString borderRadius;
|
||||||
|
[TreatNullAs=EmptyString] attribute DOMString borderSpacing;
|
||||||
[TreatNullAs=EmptyString] attribute DOMString borderStyle;
|
[TreatNullAs=EmptyString] attribute DOMString borderStyle;
|
||||||
[TreatNullAs=EmptyString] attribute DOMString borderWidth;
|
[TreatNullAs=EmptyString] attribute DOMString borderWidth;
|
||||||
[TreatNullAs=EmptyString] attribute DOMString borderBottom;
|
[TreatNullAs=EmptyString] attribute DOMString borderBottom;
|
||||||
|
|
1
components/servo/Cargo.lock
generated
1
components/servo/Cargo.lock
generated
|
@ -818,6 +818,7 @@ dependencies = [
|
||||||
"matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"mod_path 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"mod_path 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"plugins 0.0.1",
|
"plugins 0.0.1",
|
||||||
|
"rustc-serialize 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"selectors 0.1.0 (git+https://github.com/servo/rust-selectors)",
|
"selectors 0.1.0 (git+https://github.com/servo/rust-selectors)",
|
||||||
"string_cache 0.0.0 (git+https://github.com/servo/string-cache)",
|
"string_cache 0.0.0 (git+https://github.com/servo/string-cache)",
|
||||||
"string_cache_macros 0.0.0 (git+https://github.com/servo/string-cache)",
|
"string_cache_macros 0.0.0 (git+https://github.com/servo/string-cache)",
|
||||||
|
|
|
@ -36,6 +36,7 @@ git = "https://github.com/servo/string-cache"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
text_writer = "0.1.1"
|
text_writer = "0.1.1"
|
||||||
encoding = "0.2"
|
encoding = "0.2"
|
||||||
|
rustc-serialize = "0.2"
|
||||||
matches = "0.1"
|
matches = "0.1"
|
||||||
url = "0.2.16"
|
url = "0.2.16"
|
||||||
mod_path = "0.1"
|
mod_path = "0.1"
|
||||||
|
|
|
@ -14,7 +14,7 @@ use values::specified::CSSColor;
|
||||||
use values::{CSSFloat, specified};
|
use values::{CSSFloat, specified};
|
||||||
use properties::DeclaredValue::SpecifiedValue;
|
use properties::DeclaredValue::SpecifiedValue;
|
||||||
use properties::PropertyDeclaration;
|
use properties::PropertyDeclaration;
|
||||||
use properties::longhands;
|
use properties::longhands::{self, border_spacing};
|
||||||
use selector_matching::Stylist;
|
use selector_matching::Stylist;
|
||||||
|
|
||||||
use cssparser::Color;
|
use cssparser::Color;
|
||||||
|
@ -43,6 +43,8 @@ pub enum IntegerAttribute {
|
||||||
pub enum UnsignedIntegerAttribute {
|
pub enum UnsignedIntegerAttribute {
|
||||||
/// `<td border>`
|
/// `<td border>`
|
||||||
Border,
|
Border,
|
||||||
|
/// `<table cellspacing>`
|
||||||
|
CellSpacing,
|
||||||
/// `<td colspan>`
|
/// `<td colspan>`
|
||||||
ColSpan,
|
ColSpan,
|
||||||
}
|
}
|
||||||
|
@ -143,6 +145,21 @@ impl PresentationalHintSynthesis for Stylist {
|
||||||
element,
|
element,
|
||||||
matching_rules_list,
|
matching_rules_list,
|
||||||
shareable);
|
shareable);
|
||||||
|
match element.get_unsigned_integer_attribute(
|
||||||
|
UnsignedIntegerAttribute::CellSpacing) {
|
||||||
|
None => {}
|
||||||
|
Some(length) => {
|
||||||
|
let width_value = specified::Length::Absolute(Au::from_px(length as int));
|
||||||
|
matching_rules_list.vec_push(from_declaration(
|
||||||
|
PropertyDeclaration::BorderSpacing(
|
||||||
|
SpecifiedValue(
|
||||||
|
border_spacing::SpecifiedValue {
|
||||||
|
horizontal: width_value,
|
||||||
|
vertical: width_value,
|
||||||
|
}))));
|
||||||
|
*shareable = false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
name if *name == atom!("body") || *name == atom!("tr") || *name == atom!("thead") ||
|
name if *name == atom!("body") || *name == atom!("tr") || *name == atom!("thead") ||
|
||||||
*name == atom!("tbody") || *name == atom!("tfoot") => {
|
*name == atom!("tbody") || *name == atom!("tfoot") => {
|
||||||
|
|
|
@ -28,6 +28,7 @@ extern crate cssparser;
|
||||||
extern crate matches;
|
extern crate matches;
|
||||||
|
|
||||||
extern crate encoding;
|
extern crate encoding;
|
||||||
|
extern crate "rustc-serialize" as rustc_serialize;
|
||||||
extern crate string_cache;
|
extern crate string_cache;
|
||||||
extern crate selectors;
|
extern crate selectors;
|
||||||
|
|
||||||
|
|
|
@ -2020,6 +2020,89 @@ pub mod longhands {
|
||||||
|
|
||||||
${single_keyword("caption-side", "top bottom")}
|
${single_keyword("caption-side", "top bottom")}
|
||||||
|
|
||||||
|
${single_keyword("border-collapse", "separate collapse", experimental=True)}
|
||||||
|
|
||||||
|
<%self:longhand name="border-spacing">
|
||||||
|
use values::computed::{Context, ToComputedValue};
|
||||||
|
|
||||||
|
use cssparser::ToCss;
|
||||||
|
use text_writer::{self, TextWriter};
|
||||||
|
use util::geometry::Au;
|
||||||
|
|
||||||
|
pub mod computed_value {
|
||||||
|
use util::geometry::Au;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, RustcEncodable)]
|
||||||
|
pub struct T {
|
||||||
|
pub horizontal: Au,
|
||||||
|
pub vertical: Au,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct SpecifiedValue {
|
||||||
|
pub horizontal: specified::Length,
|
||||||
|
pub vertical: specified::Length,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_initial_value() -> computed_value::T {
|
||||||
|
computed_value::T {
|
||||||
|
horizontal: Au(0),
|
||||||
|
vertical: Au(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCss for SpecifiedValue {
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter {
|
||||||
|
try!(self.horizontal.to_css(dest));
|
||||||
|
try!(dest.write_str(" "));
|
||||||
|
self.vertical.to_css(dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToComputedValue for SpecifiedValue {
|
||||||
|
type ComputedValue = computed_value::T;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn to_computed_value(&self, context: &Context) -> computed_value::T {
|
||||||
|
computed_value::T {
|
||||||
|
horizontal: self.horizontal.to_computed_value(context),
|
||||||
|
vertical: self.vertical.to_computed_value(context),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
|
||||||
|
let mut lengths = [ None, None ];
|
||||||
|
for i in range(0, 2) {
|
||||||
|
match specified::Length::parse_non_negative(input) {
|
||||||
|
Err(()) => break,
|
||||||
|
Ok(length) => lengths[i] = Some(length),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if input.next().is_ok() {
|
||||||
|
return Err(())
|
||||||
|
}
|
||||||
|
match (lengths[0], lengths[1]) {
|
||||||
|
(None, None) => Err(()),
|
||||||
|
(Some(length), None) => {
|
||||||
|
Ok(SpecifiedValue {
|
||||||
|
horizontal: length,
|
||||||
|
vertical: length,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
(Some(horizontal), Some(vertical)) => {
|
||||||
|
Ok(SpecifiedValue {
|
||||||
|
horizontal: horizontal,
|
||||||
|
vertical: vertical,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
(None, Some(_)) => panic!("shouldn't happen"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</%self:longhand>
|
||||||
|
|
||||||
// CSS 2.1, Section 18 - User interface
|
// CSS 2.1, Section 18 - User interface
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -193,16 +193,19 @@ impl Stylist {
|
||||||
|
|
||||||
let mut shareable = true;
|
let mut shareable = true;
|
||||||
|
|
||||||
// Step 1: Virtual rules that are synthesized from legacy HTML attributes.
|
|
||||||
self.synthesize_presentational_hints_for_legacy_attributes(element,
|
|
||||||
applicable_declarations,
|
|
||||||
&mut shareable);
|
|
||||||
|
|
||||||
// Step 2: Normal rules.
|
// Step 1: Normal user-agent rules.
|
||||||
map.user_agent.normal.get_all_matching_rules(element,
|
map.user_agent.normal.get_all_matching_rules(element,
|
||||||
parent_bf,
|
parent_bf,
|
||||||
applicable_declarations,
|
applicable_declarations,
|
||||||
&mut shareable);
|
&mut shareable);
|
||||||
|
|
||||||
|
// Step 2: Presentational hints.
|
||||||
|
self.synthesize_presentational_hints_for_legacy_attributes(element,
|
||||||
|
applicable_declarations,
|
||||||
|
&mut shareable);
|
||||||
|
|
||||||
|
// Step 3: User and author normal rules.
|
||||||
map.user.normal.get_all_matching_rules(element,
|
map.user.normal.get_all_matching_rules(element,
|
||||||
parent_bf,
|
parent_bf,
|
||||||
applicable_declarations,
|
applicable_declarations,
|
||||||
|
@ -212,27 +215,27 @@ impl Stylist {
|
||||||
applicable_declarations,
|
applicable_declarations,
|
||||||
&mut shareable);
|
&mut shareable);
|
||||||
|
|
||||||
// Step 3: Normal style attributes.
|
// Step 4: Normal style attributes.
|
||||||
style_attribute.map(|sa| {
|
style_attribute.map(|sa| {
|
||||||
shareable = false;
|
shareable = false;
|
||||||
applicable_declarations.vec_push(
|
applicable_declarations.vec_push(
|
||||||
GenericDeclarationBlock::from_declarations(sa.normal.clone()))
|
GenericDeclarationBlock::from_declarations(sa.normal.clone()))
|
||||||
});
|
});
|
||||||
|
|
||||||
// Step 4: Author-supplied `!important` rules.
|
// Step 5: Author-supplied `!important` rules.
|
||||||
map.author.important.get_all_matching_rules(element,
|
map.author.important.get_all_matching_rules(element,
|
||||||
parent_bf,
|
parent_bf,
|
||||||
applicable_declarations,
|
applicable_declarations,
|
||||||
&mut shareable);
|
&mut shareable);
|
||||||
|
|
||||||
// Step 5: `!important` style attributes.
|
// Step 6: `!important` style attributes.
|
||||||
style_attribute.map(|sa| {
|
style_attribute.map(|sa| {
|
||||||
shareable = false;
|
shareable = false;
|
||||||
applicable_declarations.vec_push(
|
applicable_declarations.vec_push(
|
||||||
GenericDeclarationBlock::from_declarations(sa.important.clone()))
|
GenericDeclarationBlock::from_declarations(sa.important.clone()))
|
||||||
});
|
});
|
||||||
|
|
||||||
// Step 6: User and UA `!important` rules.
|
// Step 7: User and UA `!important` rules.
|
||||||
map.user.important.get_all_matching_rules(element,
|
map.user.important.get_all_matching_rules(element,
|
||||||
parent_bf,
|
parent_bf,
|
||||||
applicable_declarations,
|
applicable_declarations,
|
||||||
|
@ -277,3 +280,4 @@ impl PerPseudoElementSelectorMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,9 @@ flaky_cpu == append_style_a.html append_style_b.html
|
||||||
== border_code_tag.html border_code_tag_ref.html
|
== border_code_tag.html border_code_tag_ref.html
|
||||||
== border_radius_clip_a.html border_radius_clip_ref.html
|
== border_radius_clip_a.html border_radius_clip_ref.html
|
||||||
== border_radius_overlapping_a.html border_radius_overlapping_ref.html
|
== border_radius_overlapping_a.html border_radius_overlapping_ref.html
|
||||||
|
== border_spacing_a.html border_spacing_ref.html
|
||||||
|
== border_spacing_auto_layout_a.html border_spacing_ref.html
|
||||||
|
== border_spacing_fixed_layout_a.html border_spacing_ref.html
|
||||||
== border_style_none_a.html border_style_none_b.html
|
== border_style_none_a.html border_style_none_b.html
|
||||||
== borders_a.html borders_b.html
|
== borders_a.html borders_b.html
|
||||||
!= box_shadow_blur_a.html box_shadow_blur_ref.html
|
!= box_shadow_blur_a.html box_shadow_blur_ref.html
|
||||||
|
@ -141,6 +144,7 @@ flaky_cpu == append_style_a.html append_style_b.html
|
||||||
== issue-1324.html issue-1324-ref.html
|
== issue-1324.html issue-1324-ref.html
|
||||||
== last_child_pseudo_a.html last_child_pseudo_b.html
|
== last_child_pseudo_a.html last_child_pseudo_b.html
|
||||||
== last_of_type_pseudo_a.html last_of_type_pseudo_b.html
|
== last_of_type_pseudo_a.html last_of_type_pseudo_b.html
|
||||||
|
== legacy_cellspacing_attribute_a.html border_spacing_ref.html
|
||||||
== legacy_input_size_attribute_override_a.html legacy_input_size_attribute_override_ref.html
|
== legacy_input_size_attribute_override_a.html legacy_input_size_attribute_override_ref.html
|
||||||
== legacy_table_border_attribute_a.html legacy_table_border_attribute_ref.html
|
== legacy_table_border_attribute_a.html legacy_table_border_attribute_ref.html
|
||||||
== legacy_td_bgcolor_attribute_a.html legacy_td_bgcolor_attribute_ref.html
|
== legacy_td_bgcolor_attribute_a.html legacy_td_bgcolor_attribute_ref.html
|
||||||
|
|
33
tests/ref/border_spacing_a.html
Normal file
33
tests/ref/border_spacing_a.html
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!-- Tests that `border-spacing` works. -->
|
||||||
|
<style>
|
||||||
|
body, html {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
border: none;
|
||||||
|
border-spacing: 6px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
border-spacing: 64px; /* should have no effect */
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
border: none;
|
||||||
|
border-spacing: 100px; /* should have no effect */
|
||||||
|
padding: 0;
|
||||||
|
background: blue;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<table>
|
||||||
|
<tr><td width=32 style="height: 32px;"></td><td width=64></td></tr>
|
||||||
|
<tr><td width=32 style="height: 32px;"></td><td width=64></td></tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
35
tests/ref/border_spacing_auto_layout_a.html
Normal file
35
tests/ref/border_spacing_auto_layout_a.html
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!--
|
||||||
|
Tests that `border-spacing` with automatic table layout works when not all the column sizes
|
||||||
|
are specified.
|
||||||
|
-->
|
||||||
|
<style>
|
||||||
|
body, html {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
border: none;
|
||||||
|
border-spacing: 6px;
|
||||||
|
padding: 0;
|
||||||
|
width: 114px;
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
background: blue;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<table>
|
||||||
|
<tr><td style="width: 32px; height: 32px;"></td><td> </td></tr>
|
||||||
|
<tr><td style="width: 32px; height: 32px;"></td><td> </td></tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
36
tests/ref/border_spacing_fixed_layout_a.html
Normal file
36
tests/ref/border_spacing_fixed_layout_a.html
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!--
|
||||||
|
Tests that `border-spacing` with automatic table layout works when not all the column sizes
|
||||||
|
are specified.
|
||||||
|
-->
|
||||||
|
<style>
|
||||||
|
body, html {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
border: none;
|
||||||
|
border-spacing: 6px;
|
||||||
|
padding: 0;
|
||||||
|
width: 114px;
|
||||||
|
table-layout: fixed;
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
background: blue;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<table>
|
||||||
|
<tr><td style="width: 32px; height: 32px;"></td><td> </td></tr>
|
||||||
|
<tr><td style="width: 32px; height: 32px;"></td><td> </td></tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
39
tests/ref/border_spacing_ref.html
Normal file
39
tests/ref/border_spacing_ref.html
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!-- Tests that `border-spacing` works. -->
|
||||||
|
<style>
|
||||||
|
div {
|
||||||
|
background: blue;
|
||||||
|
position: absolute;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
#a, #c {
|
||||||
|
width: 32px;
|
||||||
|
}
|
||||||
|
#b, #d {
|
||||||
|
width: 64px;
|
||||||
|
}
|
||||||
|
#a, #b {
|
||||||
|
top: 6px;
|
||||||
|
}
|
||||||
|
#c, #d {
|
||||||
|
top: 44px;
|
||||||
|
}
|
||||||
|
#a, #c {
|
||||||
|
left: 6px;
|
||||||
|
}
|
||||||
|
#b, #d {
|
||||||
|
left: 44px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id=a></div>
|
||||||
|
<div id=b></div>
|
||||||
|
<div id=c></div>
|
||||||
|
<div id=d></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
color: red;
|
color: red;
|
||||||
float: right;
|
float: right;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
|
border-spacing: 0;
|
||||||
}
|
}
|
||||||
td {
|
td {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
30
tests/ref/legacy_cellspacing_attribute_a.html
Normal file
30
tests/ref/legacy_cellspacing_attribute_a.html
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!-- Tests that the legacy `cellspacing` attribute works. -->
|
||||||
|
<style>
|
||||||
|
body, html {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
background: blue;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<table cellspacing=6>
|
||||||
|
<tr><td width=32 style="height: 32px;"></td><td width=64></td></tr>
|
||||||
|
<tr><td width=32 style="height: 32px;"></td><td width=64></td></tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
table {
|
table {
|
||||||
width: 300px;
|
width: 300px;
|
||||||
table-layout: fixed;
|
table-layout: fixed;
|
||||||
|
border-spacing: 0;
|
||||||
}
|
}
|
||||||
td[colspan="2"] {
|
td[colspan="2"] {
|
||||||
background-color: blue;
|
background-color: blue;
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
border-spacing: 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -1,35 +1,36 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'ahem';
|
font-family: 'ahem';
|
||||||
src: url(fonts/ahem/ahem.ttf);
|
src: url(fonts/ahem/ahem.ttf);
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: 'ahem';
|
font-family: 'ahem';
|
||||||
font-size: 100px;
|
font-size: 100px;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
table {
|
table {
|
||||||
background:green;
|
background:green;
|
||||||
padding: 150px;
|
padding: 150px;
|
||||||
box-sizing: content-box;
|
box-sizing: content-box;
|
||||||
}
|
border-spacing: 0;
|
||||||
th {
|
}
|
||||||
color: yellow;
|
th {
|
||||||
padding: 0;
|
color: yellow;
|
||||||
}
|
padding: 0;
|
||||||
</style>
|
}
|
||||||
</head>
|
</style>
|
||||||
<body>
|
</head>
|
||||||
<table>
|
<body>
|
||||||
<tbody>
|
<table>
|
||||||
<tr>
|
<tbody>
|
||||||
<th>X</th>
|
<tr>
|
||||||
</tr>
|
<th>X</th>
|
||||||
</tbody>
|
</tr>
|
||||||
</table>
|
</tbody>
|
||||||
</body>
|
</table>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue