mirror of
https://github.com/servo/servo.git
synced 2025-09-30 08:39:16 +01:00
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:
commit
8e31e5f987
46 changed files with 1610 additions and 572 deletions
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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.)
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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>) {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue