auto merge of #4289 : pcwalton/servo/hacker-news, r=SimonSapin

This patch provides some of the groundwork for column spans greater than
1. It implements the column-span CSS property 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.

The parsing for the legacy bgcolor and border attributes is
implemented according to the WHATWG HTML specification.

Additionally, this patch cleans up some miscellaneous formatting issues,
refactors layout/css somewhat to eliminate needless levels of
indirection, and cleans up the handling of table rowgroups.

New Hacker News screenshot: http://i.imgur.com/hnl2a7E.png
This commit is contained in:
bors-servo 2014-12-15 19:33:46 -07:00
commit 8e31e5f987
46 changed files with 1610 additions and 572 deletions

View file

@ -31,6 +31,9 @@ path = "../net"
[dependencies.util]
path = "../util"
[dependencies.cssparser]
git = "https://github.com/servo/rust-cssparser"
[dependencies.encoding]
git = "https://github.com/lifthrasiir/rust-encoding"

View file

@ -268,6 +268,9 @@ impl StyleSharingCandidate {
return false
}
// FIXME(pcwalton): It's probably faster to iterate over all the element's attributes and
// use the {common, rare}-style-affecting-attributes tables as lookup tables.
for attribute_info in style::common_style_affecting_attributes().iter() {
match attribute_info.mode {
AttrIsPresentMode(flag) => {
@ -295,6 +298,12 @@ impl StyleSharingCandidate {
}
}
for attribute_name in style::rare_style_affecting_attributes().iter() {
if element.get_attr(&ns!(""), attribute_name).is_some() {
return false
}
}
if element.get_link().is_some() != self.link {
return false
}

View file

@ -2,27 +2,75 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Style retrieval from DOM elements.
//! Style retrieval from DOM elements.
use css::node_util::NodeUtil;
use wrapper::ThreadSafeLayoutNode;
use wrapper::{After, Before, Normal, ThreadSafeLayoutNode};
use std::mem;
use style::ComputedValues;
use sync::Arc;
/// Node mixin providing `style` method that returns a `NodeStyle`
pub trait StyledNode {
/// Returns the style results for the given node. If CSS selector matching has not yet been
/// performed, fails.
fn style<'a>(&'a self) -> &'a Arc<ComputedValues>;
/// Does this node have a computed style yet?
fn has_style(&self) -> bool;
/// Removes the style from this node.
fn unstyle(self);
}
impl<'ln> StyledNode for ThreadSafeLayoutNode<'ln> {
#[inline]
fn style<'a>(&'a self) -> &'a Arc<ComputedValues> {
self.get_css_select_results()
unsafe {
let layout_data_ref = self.borrow_layout_data();
match self.get_pseudo_element_type() {
Before(_) => {
mem::transmute(layout_data_ref.as_ref()
.unwrap()
.data
.before_style
.as_ref()
.unwrap())
}
After(_) => {
mem::transmute(layout_data_ref.as_ref()
.unwrap()
.data
.after_style
.as_ref()
.unwrap())
}
Normal => {
mem::transmute(layout_data_ref.as_ref()
.unwrap()
.shared_data
.style
.as_ref()
.unwrap())
}
}
}
}
fn has_style(&self) -> bool {
let layout_data_ref = self.borrow_layout_data();
layout_data_ref.as_ref().unwrap().shared_data.style.is_some()
}
fn unstyle(self) {
self.remove_css_select_results()
let mut layout_data_ref = self.mutate_layout_data();
let layout_data = layout_data_ref.as_mut().expect("no layout data");
let style =
match self.get_pseudo_element_type() {
Before(_) => &mut layout_data.data.before_style,
After (_) => &mut layout_data.data.after_style,
Normal => &mut layout_data.shared_data.style,
};
*style = None;
}
}

View file

@ -1,75 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use util::LayoutDataAccess;
use wrapper::ThreadSafeLayoutNode;
use wrapper::{After, Before, Normal};
use std::mem;
use style::ComputedValues;
use sync::Arc;
pub trait NodeUtil {
fn get_css_select_results<'a>(&'a self) -> &'a Arc<ComputedValues>;
fn have_css_select_results(&self) -> bool;
fn remove_css_select_results(self);
}
impl<'ln> NodeUtil for ThreadSafeLayoutNode<'ln> {
/// Returns the style results for the given node. If CSS selector
/// matching has not yet been performed, fails.
#[inline]
fn get_css_select_results<'a>(&'a self) -> &'a Arc<ComputedValues> {
unsafe {
let layout_data_ref = self.borrow_layout_data();
match self.get_pseudo_element_type() {
Before(_) => {
mem::transmute(layout_data_ref.as_ref()
.unwrap()
.data
.before_style
.as_ref()
.unwrap())
}
After(_) => {
mem::transmute(layout_data_ref.as_ref()
.unwrap()
.data
.after_style
.as_ref()
.unwrap())
}
Normal => {
mem::transmute(layout_data_ref.as_ref()
.unwrap()
.shared_data
.style
.as_ref()
.unwrap())
}
}
}
}
/// Does this node have a computed style yet?
fn have_css_select_results(&self) -> bool {
let layout_data_ref = self.borrow_layout_data();
layout_data_ref.as_ref().unwrap().shared_data.style.is_some()
}
fn remove_css_select_results(self) {
let mut layout_data_ref = self.mutate_layout_data();
let layout_data = layout_data_ref.as_mut().expect("no layout data");
let style =
match self.get_pseudo_element_type() {
Before(_) => &mut layout_data.data.before_style,
After (_) => &mut layout_data.data.after_style,
Normal => &mut layout_data.shared_data.style,
};
*style = None;
}
}

View file

@ -40,7 +40,7 @@ use script::layout_interface::{ContentBoxesQuery, ContentBoxQuery, ExitNowMsg, G
use script::layout_interface::{HitTestResponse, LayoutChan, LayoutRPC, LoadStylesheetMsg};
use script::layout_interface::{MouseOverResponse, Msg, NoQuery, PrepareToExitMsg};
use script::layout_interface::{ReapLayoutDataMsg, Reflow, ReflowForDisplay, ReflowMsg};
use script::layout_interface::{ScriptLayoutChan, TrustedNodeAddress};
use script::layout_interface::{ScriptLayoutChan, SetQuirksModeMsg, TrustedNodeAddress};
use script_traits::{SendEventMsg, ReflowEvent, ReflowCompleteMsg, OpaqueScriptLayoutChannel};
use script_traits::{ScriptControlChan, UntrustedNodeAddress};
use servo_msg::compositor_msg::Scrollable;
@ -390,6 +390,7 @@ impl LayoutTask {
match request {
AddStylesheetMsg(sheet) => self.handle_add_stylesheet(sheet, possibly_locked_rw_data),
LoadStylesheetMsg(url) => self.handle_load_stylesheet(url, possibly_locked_rw_data),
SetQuirksModeMsg => self.handle_set_quirks_mode(possibly_locked_rw_data),
GetRPCMsg(response_chan) => {
response_chan.send(box LayoutRPCImpl(self.rw_data.clone()) as
Box<LayoutRPC + Send>);
@ -500,6 +501,15 @@ impl LayoutTask {
LayoutTask::return_rw_data(possibly_locked_rw_data, rw_data);
}
/// Sets quirks mode for the document, causing the quirks mode stylesheet to be loaded.
fn handle_set_quirks_mode<'a>(&'a self,
possibly_locked_rw_data:
&mut Option<MutexGuard<'a, LayoutTaskData>>) {
let mut rw_data = self.lock_rw_data(possibly_locked_rw_data);
rw_data.stylist.add_quirks_mode_stylesheet();
LayoutTask::return_rw_data(possibly_locked_rw_data, rw_data);
}
/// Retrieves the flow tree root from the root node.
fn try_get_layout_root(&self, node: LayoutNode) -> Option<FlowRef> {
let mut layout_data_ref = node.mutate_layout_data();

View file

@ -14,6 +14,7 @@
#[phase(plugin, link)]
extern crate log;
extern crate cssparser;
extern crate geom;
extern crate gfx;
extern crate layout_traits;
@ -70,8 +71,6 @@ pub mod incremental;
pub mod wrapper;
pub mod css {
mod node_util;
pub mod matching;
pub mod node_style;
}

View file

@ -11,11 +11,12 @@ use block::{ISizeConstraintInput, ISizeConstraintSolution};
use construct::FlowConstructor;
use context::LayoutContext;
use floats::FloatKind;
use flow::{Flow, FlowClass, IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS, ImmutableFlowUtils};
use flow::{TableFlowClass};
use flow::{mod, Flow, FlowClass, IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS};
use flow::{ImmutableFlowUtils, TableFlowClass};
use fragment::{Fragment, FragmentBoundsIterator};
use layout_debug;
use model::{IntrinsicISizes, IntrinsicISizesContribution};
use table_row::CellIntrinsicInlineSize;
use table_wrapper::{TableLayout, FixedLayout, AutoLayout};
use wrapper::ThreadSafeLayoutNode;
@ -105,24 +106,53 @@ impl TableFlow {
/// 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
/// sizes.
pub fn update_column_inline_sizes(parent_inline_sizes: &mut Vec<ColumnIntrinsicInlineSize>,
child_inline_sizes: &Vec<ColumnIntrinsicInlineSize>)
-> IntrinsicISizes {
fn update_automatic_column_inline_sizes(
parent_inline_sizes: &mut Vec<ColumnIntrinsicInlineSize>,
child_cell_inline_sizes: &[CellIntrinsicInlineSize])
-> IntrinsicISizes {
let mut total_inline_sizes = IntrinsicISizes::new();
for (parent_sizes, child_sizes) in parent_inline_sizes.iter_mut()
.zip(child_inline_sizes.iter()) {
*parent_sizes = ColumnIntrinsicInlineSize {
minimum_length: max(parent_sizes.minimum_length, child_sizes.minimum_length),
percentage: parent_sizes.greatest_percentage(child_sizes),
preferred: max(parent_sizes.preferred, child_sizes.preferred),
constrained: parent_sizes.constrained || child_sizes.constrained
};
let mut column_index = 0;
for child_cell_inline_size in child_cell_inline_sizes.iter() {
for _ in range(0, child_cell_inline_size.column_span) {
if column_index < parent_inline_sizes.len() {
// We already have some intrinsic size information for this column. Merge it in
// according to the rules specified in INTRINSIC § 4.
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 = ColumnIntrinsicInlineSize {
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(ColumnIntrinsicInlineSize::new())
} else {
parent_inline_sizes.push(child_cell_inline_size.column_size)
}
}
total_inline_sizes.minimum_inline_size = total_inline_sizes.minimum_inline_size +
parent_sizes.minimum_length;
total_inline_sizes.preferred_inline_size = total_inline_sizes.preferred_inline_size +
parent_sizes.preferred;
total_inline_sizes.minimum_inline_size = total_inline_sizes.minimum_inline_size +
parent_inline_sizes[column_index].minimum_length;
total_inline_sizes.preferred_inline_size =
total_inline_sizes.preferred_inline_size +
parent_inline_sizes[column_index].preferred;
column_index += 1
}
}
total_inline_sizes
}
@ -136,6 +166,39 @@ impl TableFlow {
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);
}
/// 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<ColumnIntrinsicInlineSize>,
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 {
@ -190,50 +253,22 @@ impl Flow for TableFlow {
constrained: false,
})
}
} else if kid.is_table_rowgroup() || kid.is_table_row() {
// Read column inline-sizes from the table-row-group/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.
match self.table_layout {
FixedLayout => {
// 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_rowgroup() {
for grandkid in flow::mut_base(kid).child_iter() {
TableFlow::update_column_inline_sizes_for_row(
grandkid,
&mut self.column_intrinsic_inline_sizes,
&mut computation,
&mut did_first_row,
self.table_layout)
}
} else if kid.is_table_row() {
TableFlow::update_column_inline_sizes_for_row(
kid,
&mut self.column_intrinsic_inline_sizes,
&mut computation,
&mut did_first_row,
self.table_layout)
}
}
@ -277,8 +312,7 @@ impl Flow for TableFlow {
// In fixed table layout, we distribute extra space among the unspecified columns
// if there are any, or among all the columns if all are specified.
self.column_computed_inline_sizes.clear();
if total_column_inline_size < content_inline_size &&
num_unspecified_inline_sizes == 0 {
if num_unspecified_inline_sizes == 0 {
let ratio = content_inline_size.to_subpx() /
total_column_inline_size.to_subpx();
for column_inline_size in self.column_intrinsic_inline_sizes.iter() {
@ -413,6 +447,16 @@ pub struct ColumnIntrinsicInlineSize {
}
impl ColumnIntrinsicInlineSize {
/// Returns a newly-initialized `ColumnIntrinsicInlineSize` with all fields blank.
pub fn new() -> ColumnIntrinsicInlineSize {
ColumnIntrinsicInlineSize {
preferred: Au(0),
minimum_length: Au(0),
percentage: 0.0,
constrained: false,
}
}
/// Returns the true minimum size of this column, given the containing block's inline size.
/// Beware that this is generally only correct for fixed table layout. (Compare CSS 2.1 §
/// 17.5.2.1 with the algorithm in INTRINSIC § 4.)

View file

@ -17,7 +17,7 @@ use wrapper::ThreadSafeLayoutNode;
use servo_util::geometry::Au;
use std::fmt;
use style::ComputedValues;
use style::{ColSpanUnsignedIntegerAttribute, ComputedValues};
use sync::Arc;
/// A table formatting context.
@ -25,13 +25,17 @@ use sync::Arc;
pub struct TableCellFlow {
/// Data common to all block flows.
pub block_flow: BlockFlow,
/// The column span of this cell.
pub column_span: u32,
}
impl TableCellFlow {
pub fn from_node_and_fragment(node: &ThreadSafeLayoutNode, fragment: Fragment)
-> TableCellFlow {
TableCellFlow {
block_flow: BlockFlow::from_node_and_fragment(node, fragment)
block_flow: BlockFlow::from_node_and_fragment(node, fragment),
column_span: node.get_unsigned_integer_attribute(ColSpanUnsignedIntegerAttribute)
.unwrap_or(1),
}
}

View file

@ -30,20 +30,29 @@ use sync::Arc;
pub struct TableRowFlow {
pub block_flow: BlockFlow,
/// Information about the intrinsic inline-sizes of each column.
pub column_intrinsic_inline_sizes: Vec<ColumnIntrinsicInlineSize>,
/// Information about the intrinsic inline-sizes of each cell.
pub cell_intrinsic_inline_sizes: Vec<CellIntrinsicInlineSize>,
/// Information about the computed inline-sizes of each column.
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: ColumnIntrinsicInlineSize,
/// The column span of this cell.
pub column_span: u32,
}
impl TableRowFlow {
pub fn from_node_and_fragment(node: &ThreadSafeLayoutNode,
fragment: Fragment)
-> TableRowFlow {
TableRowFlow {
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(),
}
}
@ -53,8 +62,8 @@ impl TableRowFlow {
-> TableRowFlow {
TableRowFlow {
block_flow: BlockFlow::from_node(constructor, node),
column_intrinsic_inline_sizes: Vec::new(),
column_computed_inline_sizes: Vec::new(),
cell_intrinsic_inline_sizes: Vec::new(),
column_computed_inline_sizes: Vec::new()
}
}
@ -156,7 +165,7 @@ impl Flow for TableRowFlow {
}
fn column_intrinsic_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<ColumnIntrinsicInlineSize> {
&mut self.column_intrinsic_inline_sizes
panic!("can't call column_intrinsic_inline_sizes() on table row")
}
fn column_computed_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<ColumnComputedInlineSize> {
@ -181,10 +190,15 @@ impl Flow for TableRowFlow {
// Collect the specified column inline-size of the cell. This is used in both fixed and
// automatic table layout calculation.
let child_specified_inline_size = kid.as_table_cell()
.fragment()
.style()
.content_inline_size();
let child_specified_inline_size;
let child_column_span;
{
let child_table_cell = kid.as_table_cell();
child_specified_inline_size = child_table_cell.fragment()
.style()
.content_inline_size();
child_column_span = child_table_cell.column_span
}
// Collect minimum and preferred inline-sizes of the cell for automatic table layout
// calculation.
@ -208,15 +222,16 @@ impl Flow for TableRowFlow {
};
min_inline_size = min_inline_size + child_column_inline_size.minimum_length;
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.preferred_inline_size = max(min_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) {
let _scope = layout_debug_scope!("table_row::assign_inline_sizes {:x}",
self.block_flow.base.debug_id());
@ -233,11 +248,46 @@ impl Flow for TableRowFlow {
layout_context,
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_computed_inline_size_iterator = self.column_computed_inline_sizes.iter();
for cell_intrinsic_inline_size in self.cell_intrinsic_inline_sizes.iter() {
// Start with the computed inline size for the first column in the span.
let mut column_computed_inline_size =
match column_computed_inline_size_iterator.next() {
Some(column_computed_inline_size) => *column_computed_inline_size,
None => {
// We're in fixed layout mode and there are more cells in this row than
// columns we know about. According to CSS 2.1 § 17.5.2.1, the behavior is
// now undefined. So just use zero.
//
// FIXME(pcwalton): $10 says this isn't Web compatible.
ColumnComputedInlineSize {
size: Au(0),
}
}
};
// 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_computed_inline_size =
match column_computed_inline_size_iterator.next() {
Some(column_computed_inline_size) => column_computed_inline_size,
None => break,
};
column_computed_inline_size.size = column_computed_inline_size.size +
extra_column_computed_inline_size.size;
}
computed_inline_size_for_cells.push(column_computed_inline_size)
}
// Push those inline sizes down to the cells.
self.block_flow.propagate_assigned_inline_size_to_children(
layout_context,
inline_start_content_edge,
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>) {

View file

@ -9,11 +9,10 @@
use block::{BlockFlow, ISizeAndMarginsComputer, MarginsMayNotCollapse};
use construct::FlowConstructor;
use context::LayoutContext;
use flow::{TableRowGroupFlowClass, FlowClass, Flow, ImmutableFlowUtils};
use flow::{Flow, FlowClass, TableRowGroupFlowClass};
use fragment::{Fragment, FragmentBoundsIterator};
use layout_debug;
use model::IntrinsicISizesContribution;
use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, InternalTable, TableFlow};
use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, InternalTable};
use wrapper::ThreadSafeLayoutNode;
use servo_util::geometry::Au;
@ -24,6 +23,7 @@ use sync::Arc;
/// A table formatting context.
#[deriving(Encodable)]
pub struct TableRowGroupFlow {
/// Fields common to all block flows.
pub block_flow: BlockFlow,
/// Information about the intrinsic inline-sizes of each column.
@ -91,54 +91,11 @@ impl Flow for TableRowGroupFlow {
&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) {
let _scope = layout_debug_scope!("table_rowgroup::bubble_inline_sizes {:x}",
self.block_flow.base.debug_id());
let mut computation = IntrinsicISizesContribution::new();
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()
// Proper calculation of intrinsic sizes in table layout requires access to the entire
// table, which we don't have yet. Defer to our parent.
}
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments.

View file

@ -31,7 +31,7 @@ use style::{ComputedValues, CSSFloat};
use style::computed_values::table_layout;
use sync::Arc;
#[deriving(Encodable)]
#[deriving(Encodable, Show)]
pub enum TableLayout {
FixedLayout,
AutoLayout

View file

@ -36,6 +36,7 @@ use incremental::RestyleDamage;
use util::{LayoutDataAccess, LayoutDataFlags, LayoutDataWrapper, OpaqueNodeMethods};
use util::{PrivateLayoutData};
use cssparser::RGBA;
use gfx::display_list::OpaqueNode;
use script::dom::bindings::codegen::InheritTypes::{ElementCast, HTMLIFrameElementCast};
use script::dom::bindings::codegen::InheritTypes::{HTMLImageElementCast, HTMLInputElementCast};
@ -56,11 +57,12 @@ use servo_msg::constellation_msg::{PipelineId, SubpageId};
use servo_util::str::{LengthOrPercentageOrAuto, is_whitespace};
use std::kinds::marker::ContravariantLifetime;
use std::mem;
use style::computed_values::{content, display, white_space};
use style::{AnyNamespace, AttrSelector, IntegerAttribute, LengthAttribute};
use style::{PropertyDeclarationBlock, SpecificNamespace, TElement, TElementAttributes, TNode};
use url::Url;
use string_cache::{Atom, Namespace};
use style::computed_values::{content, display, white_space};
use style::{AnyNamespace, AttrSelector, BorderUnsignedIntegerAttribute, IntegerAttribute};
use style::{LengthAttribute, PropertyDeclarationBlock, SimpleColorAttribute, SpecificNamespace};
use style::{TElement, TElementAttributes, TNode, UnsignedIntegerAttribute};
use url::Url;
use std::cell::{Ref, RefMut};
@ -580,6 +582,17 @@ impl<'le> TElement<'le> for LayoutElement<'le> {
}
}
}
#[inline]
fn has_nonzero_border(self) -> bool {
unsafe {
match self.element
.get_unsigned_integer_attribute_for_layout(BorderUnsignedIntegerAttribute) {
None | Some(0) => false,
_ => true,
}
}
}
}
impl<'le> TElementAttributes for LayoutElement<'le> {
@ -594,6 +607,18 @@ impl<'le> TElementAttributes for LayoutElement<'le> {
self.element.get_integer_attribute_for_layout(integer_attribute)
}
}
fn get_unsigned_integer_attribute(self, attribute: UnsignedIntegerAttribute) -> Option<u32> {
unsafe {
self.element.get_unsigned_integer_attribute_for_layout(attribute)
}
}
fn get_simple_color_attribute(self, attribute: SimpleColorAttribute) -> Option<RGBA> {
unsafe {
self.element.get_simple_color_attribute_for_layout(attribute)
}
}
}
fn get_content(content_list: &content::T) -> String {
@ -913,6 +938,18 @@ impl<'ln> ThreadSafeLayoutNode<'ln> {
}
}
pub fn get_unsigned_integer_attribute(self, attribute: UnsignedIntegerAttribute)
-> Option<u32> {
unsafe {
match ElementCast::to_js(self.get_jsmanaged()) {
Some(element) => {
(*element.unsafe_get()).get_unsigned_integer_attribute_for_layout(attribute)
}
None => panic!("not an element!")
}
}
}
/// Get the description of how to account for recent style changes.
/// This is a simple bitfield and fine to copy by value.
pub fn restyle_damage(self) -> RestyleDamage {