mirror of
https://github.com/servo/servo.git
synced 2025-08-04 21:20:23 +01:00
style: Implement basic column spans.
This patch provides some of the groundwork for column spans greater than 1. It implements the column-span CSS property (prefixed so as not to be exposed to content) as well as the corresponding colspan attribute; although the former is not well-specified outside of CSS multi-column layout, INTRINSIC refers to it. Although width is distributed to spanning columns, they do not yet contribute minimum and preferred widths; this will be implemented in a follow-up. Additionally, this patch cleans up some miscellaneous formatting issues and improves the handling of table rowgroups.
This commit is contained in:
parent
14bafb11be
commit
56b78de5bc
13 changed files with 269 additions and 129 deletions
|
@ -11,11 +11,12 @@ use block::{ISizeConstraintInput, ISizeConstraintSolution};
|
||||||
use construct::FlowConstructor;
|
use construct::FlowConstructor;
|
||||||
use context::LayoutContext;
|
use context::LayoutContext;
|
||||||
use floats::FloatKind;
|
use floats::FloatKind;
|
||||||
use flow::{Flow, FlowClass, IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS, ImmutableFlowUtils};
|
use flow::{mod, Flow, FlowClass, IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS};
|
||||||
use flow::{TableFlowClass};
|
use flow::{ImmutableFlowUtils, TableFlowClass};
|
||||||
use fragment::{Fragment, FragmentBoundsIterator};
|
use fragment::{Fragment, FragmentBoundsIterator};
|
||||||
use layout_debug;
|
use layout_debug;
|
||||||
use model::{IntrinsicISizes, IntrinsicISizesContribution};
|
use model::{IntrinsicISizes, IntrinsicISizesContribution};
|
||||||
|
use table_row::CellIntrinsicInlineSize;
|
||||||
use table_wrapper::{TableLayout, FixedLayout, AutoLayout};
|
use table_wrapper::{TableLayout, FixedLayout, AutoLayout};
|
||||||
use wrapper::ThreadSafeLayoutNode;
|
use wrapper::ThreadSafeLayoutNode;
|
||||||
|
|
||||||
|
@ -105,24 +106,52 @@ impl TableFlow {
|
||||||
/// Update the corresponding value of `self_inline_sizes` if a value of `kid_inline_sizes` has
|
/// Update the corresponding value of `self_inline_sizes` if a value of `kid_inline_sizes` has
|
||||||
/// a larger value than one of `self_inline_sizes`. Returns the minimum and preferred inline
|
/// a larger value than one of `self_inline_sizes`. Returns the minimum and preferred inline
|
||||||
/// sizes.
|
/// sizes.
|
||||||
pub fn update_column_inline_sizes(parent_inline_sizes: &mut Vec<ColumnIntrinsicInlineSize>,
|
fn update_automatic_column_inline_sizes(parent_inline_sizes: &mut Vec<ColumnInlineSize>,
|
||||||
child_inline_sizes: &Vec<ColumnIntrinsicInlineSize>)
|
child_cell_inline_sizes: &[CellIntrinsicInlineSize])
|
||||||
-> IntrinsicISizes {
|
-> IntrinsicISizes {
|
||||||
let mut total_inline_sizes = IntrinsicISizes::new();
|
let mut total_inline_sizes = IntrinsicISizes::new();
|
||||||
for (parent_sizes, child_sizes) in parent_inline_sizes.iter_mut()
|
let mut column_index = 0;
|
||||||
.zip(child_inline_sizes.iter()) {
|
for child_cell_inline_size in child_cell_inline_sizes.iter() {
|
||||||
*parent_sizes = ColumnIntrinsicInlineSize {
|
for _ in range(0, child_cell_inline_size.column_span) {
|
||||||
minimum_length: max(parent_sizes.minimum_length, child_sizes.minimum_length),
|
if column_index < parent_inline_sizes.len() {
|
||||||
percentage: parent_sizes.greatest_percentage(child_sizes),
|
// We already have some intrinsic size information for this column. Merge it in
|
||||||
preferred: max(parent_sizes.preferred, child_sizes.preferred),
|
// according to the rules specified in INTRINSIC § 4.
|
||||||
constrained: parent_sizes.constrained || child_sizes.constrained
|
let parent_sizes = &mut parent_inline_sizes[column_index];
|
||||||
};
|
if child_cell_inline_size.column_span > 1 {
|
||||||
|
// TODO(pcwalton): Perform the recursive algorithm specified in INTRINSIC §
|
||||||
|
// 4. For now we make this column contribute no width.
|
||||||
|
} else {
|
||||||
|
let column_size = &child_cell_inline_size.column_size;
|
||||||
|
*parent_sizes = ColumnInlineSize {
|
||||||
|
minimum_length: max(parent_sizes.minimum_length,
|
||||||
|
column_size.minimum_length),
|
||||||
|
percentage: parent_sizes.greatest_percentage(column_size),
|
||||||
|
preferred: max(parent_sizes.preferred, column_size.preferred),
|
||||||
|
constrained: parent_sizes.constrained || column_size.constrained,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We discovered a new column. Initialize its data.
|
||||||
|
debug_assert!(column_index == parent_inline_sizes.len());
|
||||||
|
if child_cell_inline_size.column_span > 1 {
|
||||||
|
// TODO(pcwalton): Perform the recursive algorithm specified in INTRINSIC §
|
||||||
|
// 4. For now we make this column contribute no width.
|
||||||
|
parent_inline_sizes.push(ColumnInlineSize::new())
|
||||||
|
} else {
|
||||||
|
parent_inline_sizes.push(child_cell_inline_size.column_size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
total_inline_sizes.minimum_inline_size = total_inline_sizes.minimum_inline_size +
|
total_inline_sizes.minimum_inline_size = total_inline_sizes.minimum_inline_size +
|
||||||
parent_sizes.minimum_length;
|
parent_inline_sizes[column_index].minimum_length;
|
||||||
total_inline_sizes.preferred_inline_size = total_inline_sizes.preferred_inline_size +
|
total_inline_sizes.preferred_inline_size =
|
||||||
parent_sizes.preferred;
|
total_inline_sizes.preferred_inline_size +
|
||||||
|
parent_inline_sizes[column_index].preferred;
|
||||||
|
|
||||||
|
column_index += 1
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
total_inline_sizes
|
total_inline_sizes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,6 +165,39 @@ impl TableFlow {
|
||||||
fn assign_block_size_table_base<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
|
fn assign_block_size_table_base<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
|
||||||
self.block_flow.assign_block_size_block_base(layout_context, MarginsMayNotCollapse);
|
self.block_flow.assign_block_size_block_base(layout_context, MarginsMayNotCollapse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
fn update_column_inline_sizes_for_row(child: &mut Flow,
|
||||||
|
column_inline_sizes: &mut Vec<ColumnInlineSize>,
|
||||||
|
computation: &mut IntrinsicISizesContribution,
|
||||||
|
did_first_row: &mut 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.
|
||||||
|
//
|
||||||
|
// FIXME: Need to read inline-sizes from either table-header-group OR the first table-row.
|
||||||
|
debug_assert!(child.is_table_row());
|
||||||
|
let row = child.as_table_row();
|
||||||
|
match table_layout {
|
||||||
|
FixedLayout => {
|
||||||
|
// 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;
|
||||||
|
for cell_inline_size in row.cell_intrinsic_inline_sizes.iter() {
|
||||||
|
column_inline_sizes.push(cell_inline_size.column_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AutoLayout => {
|
||||||
|
computation.union_block(&TableFlow::update_automatic_column_inline_sizes(
|
||||||
|
column_inline_sizes,
|
||||||
|
row.cell_intrinsic_inline_sizes.as_slice()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Flow for TableFlow {
|
impl Flow for TableFlow {
|
||||||
|
@ -190,50 +252,20 @@ impl Flow for TableFlow {
|
||||||
constrained: false,
|
constrained: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else if kid.is_table_rowgroup() || kid.is_table_row() {
|
} else if kid.is_table_rowgroup() {
|
||||||
// Read column inline-sizes from the table-row-group/table-row, and assign
|
for grandkid in flow::mut_base(kid).child_iter() {
|
||||||
// inline-size=0 for the columns not defined in the column group.
|
TableFlow::update_column_inline_sizes_for_row(grandkid,
|
||||||
// FIXME: Need to read inline-sizes from either table-header-group OR the first
|
&mut self.column_inline_sizes,
|
||||||
// table-row.
|
&mut computation,
|
||||||
match self.table_layout {
|
&mut did_first_row,
|
||||||
FixedLayout => {
|
self.table_layout)
|
||||||
// Fixed table layout only looks at the first row.
|
|
||||||
if !did_first_row {
|
|
||||||
did_first_row = true;
|
|
||||||
for child_column_inline_size in kid.column_intrinsic_inline_sizes()
|
|
||||||
.iter() {
|
|
||||||
self.column_intrinsic_inline_sizes.push(*child_column_inline_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AutoLayout => {
|
|
||||||
let child_column_inline_sizes = kid.column_intrinsic_inline_sizes();
|
|
||||||
let mut child_intrinsic_sizes = TableFlow::update_column_inline_sizes(
|
|
||||||
&mut self.column_intrinsic_inline_sizes,
|
|
||||||
child_column_inline_sizes);
|
|
||||||
|
|
||||||
// Add new columns if processing this row caused us to discover them.
|
|
||||||
let child_column_count = child_column_inline_sizes.len();
|
|
||||||
let parent_column_count = self.column_intrinsic_inline_sizes.len();
|
|
||||||
debug!("table until the previous row has {} column(s) and this row has {} \
|
|
||||||
column(s)",
|
|
||||||
parent_column_count,
|
|
||||||
child_column_count);
|
|
||||||
self.column_intrinsic_inline_sizes.reserve(child_column_count);
|
|
||||||
for i in range(parent_column_count, child_column_count) {
|
|
||||||
let inline_size_for_new_column = (*child_column_inline_sizes)[i];
|
|
||||||
child_intrinsic_sizes.minimum_inline_size =
|
|
||||||
child_intrinsic_sizes.minimum_inline_size +
|
|
||||||
inline_size_for_new_column.minimum_length;
|
|
||||||
child_intrinsic_sizes.preferred_inline_size =
|
|
||||||
child_intrinsic_sizes.preferred_inline_size +
|
|
||||||
inline_size_for_new_column.preferred;
|
|
||||||
self.column_intrinsic_inline_sizes.push(inline_size_for_new_column);
|
|
||||||
}
|
|
||||||
|
|
||||||
computation.union_block(&child_intrinsic_sizes)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else if kid.is_table_row() {
|
||||||
|
TableFlow::update_column_inline_sizes_for_row(kid,
|
||||||
|
&mut self.column_inline_sizes,
|
||||||
|
&mut computation,
|
||||||
|
&mut did_first_row,
|
||||||
|
self.table_layout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,20 +30,29 @@ use sync::Arc;
|
||||||
pub struct TableRowFlow {
|
pub struct TableRowFlow {
|
||||||
pub block_flow: BlockFlow,
|
pub block_flow: BlockFlow,
|
||||||
|
|
||||||
/// Information about the intrinsic inline-sizes of each column.
|
/// Information about the intrinsic inline-sizes of each cell.
|
||||||
pub column_intrinsic_inline_sizes: Vec<ColumnIntrinsicInlineSize>,
|
pub cell_intrinsic_inline_sizes: Vec<CellIntrinsicInlineSize>,
|
||||||
|
|
||||||
/// 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>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Information about the column inline size and span for each cell.
|
||||||
|
#[deriving(Encodable)]
|
||||||
|
pub struct CellIntrinsicInlineSize {
|
||||||
|
/// Inline sizes that this cell contributes to the column.
|
||||||
|
pub column_size: ColumnInlineSize,
|
||||||
|
/// The column span of this cell.
|
||||||
|
pub column_span: u32,
|
||||||
|
}
|
||||||
|
|
||||||
impl TableRowFlow {
|
impl TableRowFlow {
|
||||||
pub fn from_node_and_fragment(node: &ThreadSafeLayoutNode,
|
pub fn from_node_and_fragment(node: &ThreadSafeLayoutNode,
|
||||||
fragment: Fragment)
|
fragment: Fragment)
|
||||||
-> TableRowFlow {
|
-> TableRowFlow {
|
||||||
TableRowFlow {
|
TableRowFlow {
|
||||||
block_flow: BlockFlow::from_node_and_fragment(node, fragment),
|
block_flow: BlockFlow::from_node_and_fragment(node, fragment),
|
||||||
column_intrinsic_inline_sizes: Vec::new(),
|
cell_intrinsic_inline_sizes: Vec::new(),
|
||||||
column_computed_inline_sizes: Vec::new(),
|
column_computed_inline_sizes: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,8 +62,8 @@ impl TableRowFlow {
|
||||||
-> TableRowFlow {
|
-> TableRowFlow {
|
||||||
TableRowFlow {
|
TableRowFlow {
|
||||||
block_flow: BlockFlow::from_node(constructor, node),
|
block_flow: BlockFlow::from_node(constructor, node),
|
||||||
column_intrinsic_inline_sizes: Vec::new(),
|
cell_intrinsic_inline_sizes: Vec::new(),
|
||||||
column_computed_inline_sizes: Vec::new(),
|
column_computed_inline_sizes: Vec::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,10 +190,13 @@ impl Flow for TableRowFlow {
|
||||||
|
|
||||||
// Collect the specified column inline-size of the cell. This is used in both fixed and
|
// Collect the specified column inline-size of the cell. This is used in both fixed and
|
||||||
// automatic table layout calculation.
|
// automatic table layout calculation.
|
||||||
let child_specified_inline_size = kid.as_table_cell()
|
let child_specified_inline_size;
|
||||||
.fragment()
|
let child_column_span;
|
||||||
.style()
|
{
|
||||||
.content_inline_size();
|
let child_style = kid.as_table_cell().fragment().style();
|
||||||
|
child_specified_inline_size = child_style.content_inline_size();
|
||||||
|
child_column_span = child_style.get_table()._servo_column_span
|
||||||
|
}
|
||||||
|
|
||||||
// Collect minimum and preferred inline-sizes of the cell for automatic table layout
|
// Collect minimum and preferred inline-sizes of the cell for automatic table layout
|
||||||
// calculation.
|
// calculation.
|
||||||
|
@ -208,15 +220,16 @@ impl Flow for TableRowFlow {
|
||||||
};
|
};
|
||||||
min_inline_size = min_inline_size + child_column_inline_size.minimum_length;
|
min_inline_size = min_inline_size + child_column_inline_size.minimum_length;
|
||||||
pref_inline_size = pref_inline_size + child_column_inline_size.preferred;
|
pref_inline_size = pref_inline_size + child_column_inline_size.preferred;
|
||||||
self.column_intrinsic_inline_sizes.push(child_column_inline_size);
|
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.minimum_inline_size = min_inline_size;
|
||||||
self.block_flow.base.intrinsic_inline_sizes.preferred_inline_size = max(min_inline_size,
|
self.block_flow.base.intrinsic_inline_sizes.preferred_inline_size = max(min_inline_size,
|
||||||
pref_inline_size);
|
pref_inline_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments.
|
|
||||||
/// When called on this context, the context has had its inline-size set by the parent context.
|
|
||||||
fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
|
fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
|
||||||
let _scope = layout_debug_scope!("table_row::assign_inline_sizes {:x}",
|
let _scope = layout_debug_scope!("table_row::assign_inline_sizes {:x}",
|
||||||
self.block_flow.base.debug_id());
|
self.block_flow.base.debug_id());
|
||||||
|
@ -233,11 +246,41 @@ impl Flow for TableRowFlow {
|
||||||
layout_context,
|
layout_context,
|
||||||
containing_block_inline_size);
|
containing_block_inline_size);
|
||||||
|
|
||||||
|
// Spread out the completed inline sizes among columns with spans > 1.
|
||||||
|
let mut computed_inline_size_for_cells = Vec::new();
|
||||||
|
let mut column_inline_size_iterator = self.column_inline_sizes.iter();
|
||||||
|
for cell_intrinsic_inline_size in self.cell_intrinsic_inline_sizes.iter() {
|
||||||
|
//(intrinsic_inline_size_for_column, computed_inline_size_for_column) in
|
||||||
|
// Start with the computed inline size for the first column in the span.
|
||||||
|
let mut column_inline_size = match column_inline_size_iterator.next() {
|
||||||
|
Some(column_inline_size) => *column_inline_size,
|
||||||
|
None => {
|
||||||
|
// This could happen if there are too few cells in this row. Don't crash.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add in computed inline sizes for any extra columns in the span.
|
||||||
|
for _ in range(1, cell_intrinsic_inline_size.column_span) {
|
||||||
|
let extra_column_inline_size = match column_inline_size_iterator.next() {
|
||||||
|
Some(column_inline_size) => column_inline_size,
|
||||||
|
None => break,
|
||||||
|
};
|
||||||
|
column_inline_size.minimum_length = column_inline_size.minimum_length +
|
||||||
|
extra_column_inline_size.minimum_length;
|
||||||
|
column_inline_size.preferred = column_inline_size.preferred +
|
||||||
|
extra_column_inline_size.preferred;
|
||||||
|
}
|
||||||
|
|
||||||
|
computed_inline_size_for_cells.push(column_inline_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push those inline sizes down to the cells.
|
||||||
self.block_flow.propagate_assigned_inline_size_to_children(
|
self.block_flow.propagate_assigned_inline_size_to_children(
|
||||||
layout_context,
|
layout_context,
|
||||||
inline_start_content_edge,
|
inline_start_content_edge,
|
||||||
containing_block_inline_size,
|
containing_block_inline_size,
|
||||||
Some(self.column_computed_inline_sizes.as_slice()));
|
Some(computed_inline_size_for_cells.as_slice()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
|
fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
|
||||||
|
|
|
@ -6,13 +6,12 @@
|
||||||
|
|
||||||
#![deny(unsafe_blocks)]
|
#![deny(unsafe_blocks)]
|
||||||
|
|
||||||
use block::{BlockFlow, ISizeAndMarginsComputer, MarginsMayNotCollapse};
|
use block::{BlockFlow, ISizeAndMarginsComputer};
|
||||||
use construct::FlowConstructor;
|
use construct::FlowConstructor;
|
||||||
use context::LayoutContext;
|
use context::LayoutContext;
|
||||||
use flow::{TableRowGroupFlowClass, FlowClass, Flow, ImmutableFlowUtils};
|
use flow::{mod, Flow, FlowClass, TableRowGroupFlowClass};
|
||||||
use fragment::{Fragment, FragmentBoundsIterator};
|
use fragment::{Fragment, FragmentBoundsIterator};
|
||||||
use layout_debug;
|
use layout_debug;
|
||||||
use model::IntrinsicISizesContribution;
|
|
||||||
use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, InternalTable, TableFlow};
|
use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, InternalTable, TableFlow};
|
||||||
use wrapper::ThreadSafeLayoutNode;
|
use wrapper::ThreadSafeLayoutNode;
|
||||||
|
|
||||||
|
@ -24,6 +23,7 @@ use sync::Arc;
|
||||||
/// A table formatting context.
|
/// A table formatting context.
|
||||||
#[deriving(Encodable)]
|
#[deriving(Encodable)]
|
||||||
pub struct TableRowGroupFlow {
|
pub struct TableRowGroupFlow {
|
||||||
|
/// Fields common to all block flows.
|
||||||
pub block_flow: BlockFlow,
|
pub block_flow: BlockFlow,
|
||||||
|
|
||||||
/// Information about the intrinsic inline-sizes of each column.
|
/// Information about the intrinsic inline-sizes of each column.
|
||||||
|
@ -91,54 +91,11 @@ impl Flow for TableRowGroupFlow {
|
||||||
&mut self.column_computed_inline_sizes
|
&mut self.column_computed_inline_sizes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Recursively (bottom-up) determines the context's preferred and minimum inline-sizes. When
|
|
||||||
/// called on this context, all child contexts have had their min/pref inline-sizes set. This
|
|
||||||
/// function must decide min/pref inline-sizes based on child context inline-sizes and
|
|
||||||
/// dimensions of any fragments it is responsible for flowing.
|
|
||||||
///
|
|
||||||
/// Min/pref inline-sizes set by this function are used in automatic table layout calculation.
|
|
||||||
///
|
|
||||||
/// Also, this function finds the specified column inline-sizes from the first row. These are
|
|
||||||
/// used in fixed table layout calculation.
|
|
||||||
fn bubble_inline_sizes(&mut self) {
|
fn bubble_inline_sizes(&mut self) {
|
||||||
let _scope = layout_debug_scope!("table_rowgroup::bubble_inline_sizes {:x}",
|
let _scope = layout_debug_scope!("table_rowgroup::bubble_inline_sizes {:x}",
|
||||||
self.block_flow.base.debug_id());
|
self.block_flow.base.debug_id());
|
||||||
|
// Proper calculation of intrinsic sizes in table layout requires access to the entire
|
||||||
let mut computation = IntrinsicISizesContribution::new();
|
// table, which we don't have yet. Defer to our parent.
|
||||||
for kid in self.block_flow.base.child_iter() {
|
|
||||||
assert!(kid.is_table_row());
|
|
||||||
|
|
||||||
// Calculate minimum and preferred inline sizes for automatic table layout.
|
|
||||||
if self.column_intrinsic_inline_sizes.is_empty() {
|
|
||||||
// We're the first row.
|
|
||||||
self.column_intrinsic_inline_sizes = kid.column_intrinsic_inline_sizes().clone();
|
|
||||||
} else {
|
|
||||||
let mut child_intrinsic_sizes =
|
|
||||||
TableFlow::update_column_inline_sizes(&mut self.column_intrinsic_inline_sizes,
|
|
||||||
kid.column_intrinsic_inline_sizes());
|
|
||||||
|
|
||||||
// update the number of column inline-sizes from table-rows.
|
|
||||||
let column_count = self.column_intrinsic_inline_sizes.len();
|
|
||||||
let child_column_count = kid.column_intrinsic_inline_sizes().len();
|
|
||||||
for i in range(column_count, child_column_count) {
|
|
||||||
let this_column_inline_size = (*kid.column_intrinsic_inline_sizes())[i];
|
|
||||||
|
|
||||||
// FIXME(pcwalton): Ignoring the percentage here seems dubious.
|
|
||||||
child_intrinsic_sizes.minimum_inline_size =
|
|
||||||
child_intrinsic_sizes.minimum_inline_size +
|
|
||||||
this_column_inline_size.minimum_length;
|
|
||||||
child_intrinsic_sizes.preferred_inline_size =
|
|
||||||
child_intrinsic_sizes.preferred_inline_size +
|
|
||||||
this_column_inline_size.preferred;
|
|
||||||
self.column_intrinsic_inline_sizes.push(this_column_inline_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
computation.union_block(&child_intrinsic_sizes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.block_flow.base.intrinsic_inline_sizes = computation.finish()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments.
|
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments.
|
||||||
|
|
|
@ -44,8 +44,9 @@ use dom::node::{window_from_node};
|
||||||
use dom::nodelist::NodeList;
|
use dom::nodelist::NodeList;
|
||||||
use dom::virtualmethods::{VirtualMethods, vtable_for};
|
use dom::virtualmethods::{VirtualMethods, vtable_for};
|
||||||
use devtools_traits::AttrInfo;
|
use devtools_traits::AttrInfo;
|
||||||
use style::{mod, BgColorSimpleColorAttribute, BorderUnsignedIntegerAttribute, IntegerAttribute};
|
use style::{mod, BgColorSimpleColorAttribute, BorderUnsignedIntegerAttribute};
|
||||||
use style::{LengthAttribute, SimpleColorAttribute, SizeIntegerAttribute, UnsignedIntegerAttribute};
|
use style::{ColSpanUnsignedIntegerAttribute, IntegerAttribute, LengthAttribute};
|
||||||
|
use style::{SimpleColorAttribute, SizeIntegerAttribute, UnsignedIntegerAttribute};
|
||||||
use style::{WidthLengthAttribute, matches, parse_selector_list_from_str};
|
use style::{WidthLengthAttribute, matches, parse_selector_list_from_str};
|
||||||
use servo_util::namespace;
|
use servo_util::namespace;
|
||||||
use servo_util::str::{DOMString, LengthOrPercentageOrAuto, SimpleColor};
|
use servo_util::str::{DOMString, LengthOrPercentageOrAuto, SimpleColor};
|
||||||
|
@ -349,6 +350,14 @@ impl RawLayoutElementHelpers for Element {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ColSpanUnsignedIntegerAttribute => {
|
||||||
|
if self.is_htmltablecellelement() {
|
||||||
|
let this: &HTMLTableCellElement = mem::transmute(self);
|
||||||
|
this.get_colspan()
|
||||||
|
} else {
|
||||||
|
panic!("I'm not a table cell!")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ pub struct HTMLTableCellElement {
|
||||||
htmlelement: HTMLElement,
|
htmlelement: HTMLElement,
|
||||||
background_color: Cell<Option<SimpleColor>>,
|
background_color: Cell<Option<SimpleColor>>,
|
||||||
border: Cell<Option<u32>>,
|
border: Cell<Option<u32>>,
|
||||||
|
colspan: Cell<Option<u32>>,
|
||||||
width: Cell<LengthOrPercentageOrAuto>,
|
width: Cell<LengthOrPercentageOrAuto>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,6 +46,7 @@ impl HTMLTableCellElement {
|
||||||
htmlelement: HTMLElement::new_inherited(type_id, tag_name, prefix, document),
|
htmlelement: HTMLElement::new_inherited(type_id, tag_name, prefix, document),
|
||||||
background_color: Cell::new(None),
|
background_color: Cell::new(None),
|
||||||
border: Cell::new(None),
|
border: Cell::new(None),
|
||||||
|
colspan: Cell::new(None),
|
||||||
width: Cell::new(AutoLpa),
|
width: Cell::new(AutoLpa),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,6 +60,7 @@ impl HTMLTableCellElement {
|
||||||
pub trait HTMLTableCellElementHelpers {
|
pub trait HTMLTableCellElementHelpers {
|
||||||
fn get_background_color(&self) -> Option<SimpleColor>;
|
fn get_background_color(&self) -> Option<SimpleColor>;
|
||||||
fn get_border(&self) -> Option<u32>;
|
fn get_border(&self) -> Option<u32>;
|
||||||
|
fn get_colspan(&self) -> Option<u32>;
|
||||||
fn get_width(&self) -> LengthOrPercentageOrAuto;
|
fn get_width(&self) -> LengthOrPercentageOrAuto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +73,10 @@ impl HTMLTableCellElementHelpers for HTMLTableCellElement {
|
||||||
self.border.get()
|
self.border.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_colspan(&self) -> Option<u32> {
|
||||||
|
self.colspan.get()
|
||||||
|
}
|
||||||
|
|
||||||
fn get_width(&self) -> LengthOrPercentageOrAuto {
|
fn get_width(&self) -> LengthOrPercentageOrAuto {
|
||||||
self.width.get()
|
self.width.get()
|
||||||
}
|
}
|
||||||
|
@ -97,6 +104,9 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLTableCellElement> {
|
||||||
.as_slice()
|
.as_slice()
|
||||||
.chars()).unwrap_or(1)))
|
.chars()).unwrap_or(1)))
|
||||||
}
|
}
|
||||||
|
&atom!("colspan") => {
|
||||||
|
self.colspan.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())),
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
|
@ -111,6 +121,7 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLTableCellElement> {
|
||||||
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!("colspan") => self.colspan.set(None),
|
||||||
&atom!("width") => self.width.set(AutoLpa),
|
&atom!("width") => self.width.set(AutoLpa),
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,6 +108,7 @@ impl HTMLTableElementHelpers for HTMLTableElement {
|
||||||
fn get_border(&self) -> Option<u32> {
|
fn get_border(&self) -> Option<u32> {
|
||||||
self.border.get()
|
self.border.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_width(&self) -> LengthOrPercentageOrAuto {
|
fn get_width(&self) -> LengthOrPercentageOrAuto {
|
||||||
self.width.get()
|
self.width.get()
|
||||||
}
|
}
|
||||||
|
@ -135,6 +136,7 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLTableElement> {
|
||||||
.as_slice()
|
.as_slice()
|
||||||
.chars()).unwrap_or(1)))
|
.chars()).unwrap_or(1)))
|
||||||
}
|
}
|
||||||
|
&atom!("width") => self.width.set(str::parse_length(attr.value().as_slice())),
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -148,6 +150,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!("width") => self.width.set(AutoLpa),
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,8 @@
|
||||||
use node::{TElement, TElementAttributes, TNode};
|
use node::{TElement, TElementAttributes, TNode};
|
||||||
use properties::{BackgroundColorDeclaration, BorderBottomWidthDeclaration};
|
use properties::{BackgroundColorDeclaration, BorderBottomWidthDeclaration};
|
||||||
use properties::{BorderLeftWidthDeclaration, BorderRightWidthDeclaration};
|
use properties::{BorderLeftWidthDeclaration, BorderRightWidthDeclaration};
|
||||||
use properties::{BorderTopWidthDeclaration, SpecifiedValue, WidthDeclaration, specified};
|
use properties::{BorderTopWidthDeclaration, ServoColumnSpanDeclaration, SpecifiedValue};
|
||||||
|
use properties::{WidthDeclaration, specified};
|
||||||
use selector_matching::{DeclarationBlock, Stylist};
|
use selector_matching::{DeclarationBlock, Stylist};
|
||||||
|
|
||||||
use cssparser::{RGBA, RGBAColor};
|
use cssparser::{RGBA, RGBAColor};
|
||||||
|
@ -32,6 +33,8 @@ pub enum IntegerAttribute {
|
||||||
pub enum UnsignedIntegerAttribute {
|
pub enum UnsignedIntegerAttribute {
|
||||||
/// `<td border>`
|
/// `<td border>`
|
||||||
BorderUnsignedIntegerAttribute,
|
BorderUnsignedIntegerAttribute,
|
||||||
|
/// `<td colspan>`
|
||||||
|
ColSpanUnsignedIntegerAttribute,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Legacy presentational attributes that take a simple color as defined in HTML5 § 2.4.6.
|
/// Legacy presentational attributes that take a simple color as defined in HTML5 § 2.4.6.
|
||||||
|
@ -111,6 +114,14 @@ impl PresentationalHintSynthesis for Stylist {
|
||||||
*shareable = false
|
*shareable = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
match element.get_unsigned_integer_attribute(ColSpanUnsignedIntegerAttribute) {
|
||||||
|
None => {}
|
||||||
|
Some(value) => {
|
||||||
|
matching_rules_list.vec_push(DeclarationBlock::from_declaration(
|
||||||
|
ServoColumnSpanDeclaration(SpecifiedValue(value))));
|
||||||
|
*shareable = false
|
||||||
|
}
|
||||||
|
}
|
||||||
self.synthesize_presentational_hint_for_legacy_background_color_attribute(
|
self.synthesize_presentational_hint_for_legacy_background_color_attribute(
|
||||||
element,
|
element,
|
||||||
matching_rules_list,
|
matching_rules_list,
|
||||||
|
|
|
@ -54,9 +54,10 @@ pub use selectors::{PseudoElement, Before, After, SelectorList, parse_selector_l
|
||||||
pub use selectors::{AttrSelector, NamespaceConstraint, SpecificNamespace, AnyNamespace};
|
pub use selectors::{AttrSelector, NamespaceConstraint, SpecificNamespace, AnyNamespace};
|
||||||
pub use selectors::{SimpleSelector, LocalNameSelector};
|
pub use selectors::{SimpleSelector, LocalNameSelector};
|
||||||
pub use cssparser::{Color, RGBA};
|
pub use cssparser::{Color, RGBA};
|
||||||
pub use legacy::{BgColorSimpleColorAttribute, BorderUnsignedIntegerAttribute, IntegerAttribute};
|
pub use legacy::{BgColorSimpleColorAttribute, BorderUnsignedIntegerAttribute};
|
||||||
pub use legacy::{LengthAttribute, SimpleColorAttribute, SizeIntegerAttribute};
|
pub use legacy::{ColSpanUnsignedIntegerAttribute, IntegerAttribute, LengthAttribute};
|
||||||
pub use legacy::{UnsignedIntegerAttribute, WidthLengthAttribute};
|
pub use legacy::{SimpleColorAttribute, SizeIntegerAttribute, UnsignedIntegerAttribute};
|
||||||
|
pub use legacy::{WidthLengthAttribute};
|
||||||
pub use font_face::{Source, LocalSource, UrlSource_};
|
pub use font_face::{Source, LocalSource, UrlSource_};
|
||||||
|
|
||||||
mod stylesheets;
|
mod stylesheets;
|
||||||
|
|
|
@ -1316,6 +1316,34 @@ pub mod longhands {
|
||||||
|
|
||||||
${single_keyword("table-layout", "auto fixed")}
|
${single_keyword("table-layout", "auto fixed")}
|
||||||
|
|
||||||
|
<%self:single_component_value name="-servo-column-span">
|
||||||
|
// The handling of this property is not well-specified by INTRINSIC, but its presence is
|
||||||
|
// assumed. HTML5 14.3.9 specifies that the `colspan` attribute is to be a nonnegative
|
||||||
|
// integer.
|
||||||
|
pub use super::computed_as_specified as to_computed_value;
|
||||||
|
pub mod computed_value {
|
||||||
|
pub type T = u32;
|
||||||
|
}
|
||||||
|
pub type SpecifiedValue = computed_value::T;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_initial_value() -> computed_value::T {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_component_value(input: &ComponentValue, _: &Url) -> Result<SpecifiedValue,()> {
|
||||||
|
match input {
|
||||||
|
&Number(ref value) => {
|
||||||
|
match value.int_value {
|
||||||
|
None => Err(()),
|
||||||
|
Some(n) => Ok(n as SpecifiedValue),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</%self:single_component_value>
|
||||||
|
|
||||||
// CSS 2.1, Section 18 - User interface
|
// CSS 2.1, Section 18 - User interface
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -802,8 +802,8 @@ pub fn common_style_affecting_attributes() -> [CommonStyleAffectingAttributeInfo
|
||||||
/// Attributes that, if present, disable style sharing. All legacy HTML attributes must be in
|
/// Attributes that, if present, disable style sharing. All legacy HTML attributes must be in
|
||||||
/// either this list or `common_style_affecting_attributes`. See the comment in
|
/// either this list or `common_style_affecting_attributes`. See the comment in
|
||||||
/// `synthesize_presentational_hints_for_legacy_attributes`.
|
/// `synthesize_presentational_hints_for_legacy_attributes`.
|
||||||
pub fn rare_style_affecting_attributes() -> [Atom, ..2] {
|
pub fn rare_style_affecting_attributes() -> [Atom, ..3] {
|
||||||
[ atom!("bgcolor"), atom!("border") ]
|
[ atom!("bgcolor"), atom!("border"), atom!("colspan") ]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines whether the given element matches the given single selector.
|
/// Determines whether the given element matches the given single selector.
|
||||||
|
|
|
@ -209,5 +209,6 @@ fragment=top != ../html/acid2.html acid2_ref.html
|
||||||
== box_shadow_inset_parsing_a.html box_shadow_inset_parsing_ref.html
|
== box_shadow_inset_parsing_a.html box_shadow_inset_parsing_ref.html
|
||||||
!= list_style_type_a.html list_style_type_ref.html
|
!= list_style_type_a.html list_style_type_ref.html
|
||||||
== list_style_position_a.html list_style_position_ref.html
|
== list_style_position_a.html list_style_position_ref.html
|
||||||
|
== table_colspan_simple_a.html table_colspan_simple_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
|
||||||
== legacy_table_border_attribute_a.html legacy_table_border_attribute_ref.html
|
== legacy_table_border_attribute_a.html legacy_table_border_attribute_ref.html
|
||||||
|
|
22
tests/ref/table_colspan_simple_a.html
Normal file
22
tests/ref/table_colspan_simple_a.html
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<style>
|
||||||
|
td[colspan="2"] {
|
||||||
|
background-color: blue;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
td[colspan="3"] {
|
||||||
|
background-color: green;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<body>
|
||||||
|
<table border=0 cellspacing=0 cellpadding=0>
|
||||||
|
<tr><td width=100> </td><td width=100> </td><td width=100> </td></tr>
|
||||||
|
<tr><td colspan=2> </td><td> </td></tr>
|
||||||
|
<tr><td> <td colspan=2> </td></tr>
|
||||||
|
<tr><td colspan=3> </td></tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
22
tests/ref/table_colspan_simple_ref.html
Normal file
22
tests/ref/table_colspan_simple_ref.html
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<style>
|
||||||
|
td.two {
|
||||||
|
background-color: blue;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
td.three {
|
||||||
|
background-color: green;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<body>
|
||||||
|
<table border=0 cellspacing=0 cellpadding=0>
|
||||||
|
<tr><td width=100> </td><td width=100> </td><td width=100> </td></tr>
|
||||||
|
<tr><td class=two> </td><td class=two></td><td> </td></tr>
|
||||||
|
<tr><td> <td class=two> </td><td class=two></td></tr>
|
||||||
|
<tr><td class=three> </td><td class=three></td><td class=three></td></tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue