mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
Support a part of anonymous table(step 1-1, 1-2, 2).
Not Covered: step 1-3, 1-4, 3-1, 3-2 *Spec: http://www.w3.org/TR/CSS21/tables.html#anonymous-boxes
This commit is contained in:
parent
008be170d4
commit
48b36e5b3a
4 changed files with 191 additions and 37 deletions
|
@ -29,7 +29,7 @@ use servo_util::str::is_whitespace;
|
||||||
use std::cast;
|
use std::cast;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::num::Zero;
|
use std::num::Zero;
|
||||||
use style::{ComputedValues, TElement, TNode};
|
use style::{ComputedValues, TElement, TNode, cascade, initial_values};
|
||||||
use style::computed_values::{LengthOrPercentage, LengthOrPercentageOrAuto, overflow, LPA_Auto};
|
use style::computed_values::{LengthOrPercentage, LengthOrPercentageOrAuto, overflow, LPA_Auto};
|
||||||
use style::computed_values::{border_style, clear, font_family, line_height, position};
|
use style::computed_values::{border_style, clear, font_family, line_height, position};
|
||||||
use style::computed_values::{text_align, text_decoration, vertical_align, visibility, white_space};
|
use style::computed_values::{text_align, text_decoration, vertical_align, visibility, white_space};
|
||||||
|
@ -446,6 +446,33 @@ impl Box {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Constructs a new `Box` instance for an anonymous table object.
|
||||||
|
pub fn new_anonymous_table_box(node: &ThreadSafeLayoutNode, specific: SpecificBoxInfo) -> Box {
|
||||||
|
// CSS 2.1 § 17.2.1 This is for non-inherited properties on anonymous table boxes
|
||||||
|
// example:
|
||||||
|
//
|
||||||
|
// <div style="display: table">
|
||||||
|
// Foo
|
||||||
|
// </div>
|
||||||
|
//
|
||||||
|
// Anonymous table boxes, TableRowBox and TableCellBox, are generated around `Foo`, but it shouldn't inherit the border.
|
||||||
|
|
||||||
|
let (node_style, _) = cascade(&[], false, Some(node.style().get()),
|
||||||
|
&initial_values(), None);
|
||||||
|
Box {
|
||||||
|
node: OpaqueNode::from_thread_safe_layout_node(node),
|
||||||
|
style: Arc::new(node_style),
|
||||||
|
border_box: RefCell::new(Au::zero_rect()),
|
||||||
|
border: RefCell::new(Zero::zero()),
|
||||||
|
padding: RefCell::new(Zero::zero()),
|
||||||
|
margin: RefCell::new(Zero::zero()),
|
||||||
|
specific: specific,
|
||||||
|
position_offsets: RefCell::new(Zero::zero()),
|
||||||
|
inline_info: RefCell::new(None),
|
||||||
|
new_line_pos: ~[],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Constructs a new `Box` instance from an opaque node.
|
/// Constructs a new `Box` instance from an opaque node.
|
||||||
pub fn from_opaque_node_and_style(node: OpaqueNode,
|
pub fn from_opaque_node_and_style(node: OpaqueNode,
|
||||||
style: Arc<ComputedValues>,
|
style: Arc<ComputedValues>,
|
||||||
|
|
|
@ -313,15 +313,17 @@ impl<'a> FlowConstructor<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an inline flow from a set of inline boxes and adds it as a child of the given flow.
|
/// Creates an inline flow from a set of inline boxes, then adds it as a child of the given flow
|
||||||
|
/// or pushes it onto the given flow list.
|
||||||
///
|
///
|
||||||
/// `#[inline(always)]` because this is performance critical and LLVM will not inline it
|
/// `#[inline(always)]` because this is performance critical and LLVM will not inline it
|
||||||
/// otherwise.
|
/// otherwise.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn flush_inline_boxes_to_flow(&mut self,
|
fn flush_inline_boxes_to_flow_or_list(&mut self,
|
||||||
boxes: ~[Box],
|
boxes: ~[Box],
|
||||||
flow: &mut ~Flow,
|
flow: &mut ~Flow,
|
||||||
node: &ThreadSafeLayoutNode) {
|
flow_list: &mut ~[~Flow],
|
||||||
|
node: &ThreadSafeLayoutNode) {
|
||||||
if boxes.len() == 0 {
|
if boxes.len() == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -330,18 +332,23 @@ impl<'a> FlowConstructor<'a> {
|
||||||
TextRunScanner::new().scan_for_runs(self.font_context(), inline_flow);
|
TextRunScanner::new().scan_for_runs(self.font_context(), inline_flow);
|
||||||
inline_flow.finish(self.layout_context);
|
inline_flow.finish(self.layout_context);
|
||||||
|
|
||||||
flow.add_new_child(inline_flow)
|
if flow.need_anonymous_flow(inline_flow) {
|
||||||
|
flow_list.push(inline_flow)
|
||||||
|
} else {
|
||||||
|
flow.add_new_child(inline_flow)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an inline flow from a set of inline boxes, if present, and adds it as a child of
|
/// Creates an inline flow from a set of inline boxes, if present, and adds it as a child of
|
||||||
/// the given flow.
|
/// the given flow or pushes it onto the given flow list.
|
||||||
fn flush_inline_boxes_to_flow_if_necessary(&mut self,
|
fn flush_inline_boxes_to_flow_or_list_if_necessary(&mut self,
|
||||||
opt_boxes: &mut Option<~[Box]>,
|
opt_boxes: &mut Option<~[Box]>,
|
||||||
flow: &mut ~Flow,
|
flow: &mut ~Flow,
|
||||||
node: &ThreadSafeLayoutNode) {
|
flow_list: &mut ~[~Flow],
|
||||||
|
node: &ThreadSafeLayoutNode) {
|
||||||
let opt_boxes = mem::replace(opt_boxes, None);
|
let opt_boxes = mem::replace(opt_boxes, None);
|
||||||
if opt_boxes.len() > 0 {
|
if opt_boxes.len() > 0 {
|
||||||
self.flush_inline_boxes_to_flow(opt_boxes.to_vec(), flow, node)
|
self.flush_inline_boxes_to_flow_or_list(opt_boxes.to_vec(), flow, flow_list, node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,6 +365,7 @@ impl<'a> FlowConstructor<'a> {
|
||||||
-> ConstructionResult {
|
-> ConstructionResult {
|
||||||
// Gather up boxes for the inline flows we might need to create.
|
// Gather up boxes for the inline flows we might need to create.
|
||||||
let mut opt_boxes_for_inline_flow = None;
|
let mut opt_boxes_for_inline_flow = None;
|
||||||
|
let mut consecutive_siblings = ~[];
|
||||||
let mut first_box = true;
|
let mut first_box = true;
|
||||||
// List of absolute descendants, in tree order.
|
// List of absolute descendants, in tree order.
|
||||||
let mut abs_descendants = Descendants::new();
|
let mut abs_descendants = Descendants::new();
|
||||||
|
@ -368,7 +376,11 @@ impl<'a> FlowConstructor<'a> {
|
||||||
FlowConstructionResult(kid_flow, kid_abs_descendants, kid_fixed_descendants) => {
|
FlowConstructionResult(kid_flow, kid_abs_descendants, kid_fixed_descendants) => {
|
||||||
// If kid_flow is TableCaptionFlow, kid_flow should be added under TableWrapperFlow.
|
// If kid_flow is TableCaptionFlow, kid_flow should be added under TableWrapperFlow.
|
||||||
if flow.is_table() && kid_flow.is_table_caption() {
|
if flow.is_table() && kid_flow.is_table_caption() {
|
||||||
kid.set_flow_construction_result(FlowConstructionResult(kid_flow, kid_abs_descendants, kid_fixed_descendants))
|
kid.set_flow_construction_result(FlowConstructionResult(kid_flow,
|
||||||
|
Descendants::new(),
|
||||||
|
Descendants::new()))
|
||||||
|
} else if flow.need_anonymous_flow(kid_flow) {
|
||||||
|
consecutive_siblings.push(kid_flow)
|
||||||
} else {
|
} else {
|
||||||
// Strip ignorable whitespace from the start of this flow per CSS 2.1 §
|
// Strip ignorable whitespace from the start of this flow per CSS 2.1 §
|
||||||
// 9.2.1.1.
|
// 9.2.1.1.
|
||||||
|
@ -382,13 +394,18 @@ impl<'a> FlowConstructor<'a> {
|
||||||
debug!("flushing {} inline box(es) to flow A",
|
debug!("flushing {} inline box(es) to flow A",
|
||||||
opt_boxes_for_inline_flow.as_ref()
|
opt_boxes_for_inline_flow.as_ref()
|
||||||
.map_or(0, |boxes| boxes.len()));
|
.map_or(0, |boxes| boxes.len()));
|
||||||
self.flush_inline_boxes_to_flow_if_necessary(&mut opt_boxes_for_inline_flow,
|
self.flush_inline_boxes_to_flow_or_list_if_necessary(&mut opt_boxes_for_inline_flow,
|
||||||
&mut flow,
|
&mut flow,
|
||||||
node);
|
&mut consecutive_siblings,
|
||||||
|
node);
|
||||||
|
if !consecutive_siblings.is_empty() {
|
||||||
|
self.generate_anonymous_missing_child(consecutive_siblings, &mut flow, node);
|
||||||
|
consecutive_siblings = ~[];
|
||||||
|
}
|
||||||
flow.add_new_child(kid_flow);
|
flow.add_new_child(kid_flow);
|
||||||
abs_descendants.push_descendants(kid_abs_descendants);
|
|
||||||
fixed_descendants.push_descendants(kid_fixed_descendants);
|
|
||||||
}
|
}
|
||||||
|
abs_descendants.push_descendants(kid_abs_descendants);
|
||||||
|
fixed_descendants.push_descendants(kid_fixed_descendants);
|
||||||
}
|
}
|
||||||
ConstructionItemConstructionResult(InlineBoxesConstructionItem(
|
ConstructionItemConstructionResult(InlineBoxesConstructionItem(
|
||||||
InlineBoxesConstructionResult {
|
InlineBoxesConstructionResult {
|
||||||
|
@ -423,14 +440,19 @@ impl<'a> FlowConstructor<'a> {
|
||||||
opt_boxes_for_inline_flow.as_ref()
|
opt_boxes_for_inline_flow.as_ref()
|
||||||
.map_or(0,
|
.map_or(0,
|
||||||
|boxes| boxes.len()));
|
|boxes| boxes.len()));
|
||||||
self.flush_inline_boxes_to_flow_if_necessary(
|
self.flush_inline_boxes_to_flow_or_list_if_necessary(
|
||||||
&mut opt_boxes_for_inline_flow,
|
&mut opt_boxes_for_inline_flow,
|
||||||
&mut flow,
|
&mut flow,
|
||||||
|
&mut consecutive_siblings,
|
||||||
node);
|
node);
|
||||||
|
|
||||||
// Push the flow generated by the {ib} split onto our list of
|
// Push the flow generated by the {ib} split onto our list of
|
||||||
// flows.
|
// flows.
|
||||||
flow.add_new_child(kid_flow)
|
if flow.need_anonymous_flow(kid_flow) {
|
||||||
|
consecutive_siblings.push(kid_flow)
|
||||||
|
} else {
|
||||||
|
flow.add_new_child(kid_flow)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -453,9 +475,13 @@ impl<'a> FlowConstructor<'a> {
|
||||||
// Perform a final flush of any inline boxes that we were gathering up to handle {ib}
|
// Perform a final flush of any inline boxes that we were gathering up to handle {ib}
|
||||||
// splits, after stripping ignorable whitespace.
|
// splits, after stripping ignorable whitespace.
|
||||||
strip_ignorable_whitespace_from_end(&mut opt_boxes_for_inline_flow);
|
strip_ignorable_whitespace_from_end(&mut opt_boxes_for_inline_flow);
|
||||||
self.flush_inline_boxes_to_flow_if_necessary(&mut opt_boxes_for_inline_flow,
|
self.flush_inline_boxes_to_flow_or_list_if_necessary(&mut opt_boxes_for_inline_flow,
|
||||||
&mut flow,
|
&mut flow,
|
||||||
node);
|
&mut consecutive_siblings,
|
||||||
|
node);
|
||||||
|
if !consecutive_siblings.is_empty() {
|
||||||
|
self.generate_anonymous_missing_child(consecutive_siblings, &mut flow, node);
|
||||||
|
}
|
||||||
|
|
||||||
// The flow is done.
|
// The flow is done.
|
||||||
flow.finish(self.layout_context);
|
flow.finish(self.layout_context);
|
||||||
|
@ -717,26 +743,45 @@ impl<'a> FlowConstructor<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_table_wrapper_flow_using_children(&mut self,
|
/// TableCaptionFlow is populated underneath TableWrapperFlow
|
||||||
|
fn place_table_caption_under_table_wrapper(&mut self,
|
||||||
table_wrapper_flow: &mut ~Flow,
|
table_wrapper_flow: &mut ~Flow,
|
||||||
node: &ThreadSafeLayoutNode)
|
node: &ThreadSafeLayoutNode) {
|
||||||
-> (Descendants, Descendants) {
|
|
||||||
// List of absolute descendants, in tree order.
|
|
||||||
let mut abs_descendants = Descendants::new();
|
|
||||||
let mut fixed_descendants = Descendants::new();
|
|
||||||
for kid in node.children() {
|
for kid in node.children() {
|
||||||
match kid.swap_out_construction_result() {
|
match kid.swap_out_construction_result() {
|
||||||
NoConstructionResult | ConstructionItemConstructionResult(_) => {}
|
NoConstructionResult | ConstructionItemConstructionResult(_) => {}
|
||||||
FlowConstructionResult(kid_flow, kid_abs_descendants, kid_fixed_descendants) => {
|
FlowConstructionResult(kid_flow, _, _) => {
|
||||||
// Only kid flows with table-caption are matched here.
|
// Only kid flows with table-caption are matched here.
|
||||||
assert!(kid_flow.is_table_caption());
|
assert!(kid_flow.is_table_caption());
|
||||||
table_wrapper_flow.add_new_child(kid_flow);
|
table_wrapper_flow.add_new_child(kid_flow);
|
||||||
abs_descendants.push_descendants(kid_abs_descendants);
|
|
||||||
fixed_descendants.push_descendants(kid_fixed_descendants);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(abs_descendants, fixed_descendants)
|
}
|
||||||
|
|
||||||
|
/// Generates an anonymous table flow according to CSS 2.1 § 17.2.1, step 2.
|
||||||
|
/// If necessary, generate recursively another anonymous table flow.
|
||||||
|
fn generate_anonymous_missing_child(&mut self, child_flows: ~[~Flow],
|
||||||
|
flow: &mut ~Flow, node: &ThreadSafeLayoutNode) {
|
||||||
|
let mut anonymous_flow = flow.generate_missing_child_flow(node);
|
||||||
|
let mut consecutive_siblings = ~[];
|
||||||
|
for kid_flow in child_flows.move_iter() {
|
||||||
|
if anonymous_flow.need_anonymous_flow(kid_flow) {
|
||||||
|
consecutive_siblings.push(kid_flow);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if !consecutive_siblings.is_empty() {
|
||||||
|
self.generate_anonymous_missing_child(consecutive_siblings, &mut anonymous_flow, node);
|
||||||
|
consecutive_siblings = ~[];
|
||||||
|
}
|
||||||
|
anonymous_flow.add_new_child(kid_flow);
|
||||||
|
}
|
||||||
|
if !consecutive_siblings.is_empty() {
|
||||||
|
self.generate_anonymous_missing_child(consecutive_siblings, &mut anonymous_flow, node);
|
||||||
|
}
|
||||||
|
// The flow is done.
|
||||||
|
anonymous_flow.finish(self.layout_context);
|
||||||
|
flow.add_new_child(anonymous_flow);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds a flow for a node with `display: table`. This yields a `TableWrapperFlow` with possibly
|
/// Builds a flow for a node with `display: table`. This yields a `TableWrapperFlow` with possibly
|
||||||
|
@ -752,8 +797,10 @@ impl<'a> FlowConstructor<'a> {
|
||||||
// We then populate the TableWrapperFlow with TableCaptionFlow, and attach
|
// We then populate the TableWrapperFlow with TableCaptionFlow, and attach
|
||||||
// the TableFlow to the TableWrapperFlow
|
// the TableFlow to the TableWrapperFlow
|
||||||
let construction_result = self.build_flow_using_children(table_flow, node);
|
let construction_result = self.build_flow_using_children(table_flow, node);
|
||||||
let (mut abs_descendants, mut fixed_descendants)
|
self.place_table_caption_under_table_wrapper(&mut wrapper_flow, node);
|
||||||
= self.build_table_wrapper_flow_using_children(&mut wrapper_flow, node);
|
|
||||||
|
let mut abs_descendants = Descendants::new();
|
||||||
|
let mut fixed_descendants = Descendants::new();
|
||||||
|
|
||||||
// NOTE: The order of captions and table are not the same order as in the DOM tree.
|
// NOTE: The order of captions and table are not the same order as in the DOM tree.
|
||||||
// All caption blocks are placed before the table flow
|
// All caption blocks are placed before the table flow
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
|
|
||||||
use css::node_style::StyledNode;
|
use css::node_style::StyledNode;
|
||||||
use layout::block::{BlockFlow};
|
use layout::block::{BlockFlow};
|
||||||
use layout::box_::Box;
|
use layout::box_::{Box, TableRowBox, TableCellBox};
|
||||||
use layout::context::LayoutContext;
|
use layout::context::LayoutContext;
|
||||||
use layout::construct::OptVector;
|
use layout::construct::OptVector;
|
||||||
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
|
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
|
||||||
|
@ -288,6 +288,12 @@ pub trait ImmutableFlowUtils {
|
||||||
/// Returns true if this flow is one of table-related flows.
|
/// Returns true if this flow is one of table-related flows.
|
||||||
fn is_table_kind(self) -> bool;
|
fn is_table_kind(self) -> bool;
|
||||||
|
|
||||||
|
/// Returns true if anonymous flow is needed between this flow and child flow.
|
||||||
|
fn need_anonymous_flow(self, child: &Flow) -> bool;
|
||||||
|
|
||||||
|
/// Generates missing child flow of this flow.
|
||||||
|
fn generate_missing_child_flow(self, node: &ThreadSafeLayoutNode) -> ~Flow;
|
||||||
|
|
||||||
/// Returns true if this flow has no children.
|
/// Returns true if this flow has no children.
|
||||||
fn is_leaf(self) -> bool;
|
fn is_leaf(self) -> bool;
|
||||||
|
|
||||||
|
@ -899,6 +905,34 @@ impl<'a> ImmutableFlowUtils for &'a Flow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if anonymous flow is needed between this flow and child flow.
|
||||||
|
/// Spec: http://www.w3.org/TR/CSS21/tables.html#anonymous-boxes
|
||||||
|
fn need_anonymous_flow(self, child: &Flow) -> bool {
|
||||||
|
match self.class() {
|
||||||
|
TableFlowClass => !child.is_proper_table_child(),
|
||||||
|
TableRowGroupFlowClass => !child.is_table_row(),
|
||||||
|
TableRowFlowClass => !child.is_table_cell(),
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates missing child flow of this flow.
|
||||||
|
fn generate_missing_child_flow(self, node: &ThreadSafeLayoutNode) -> ~Flow {
|
||||||
|
match self.class() {
|
||||||
|
TableFlowClass | TableRowGroupFlowClass => {
|
||||||
|
let box_ = Box::new_anonymous_table_box(node, TableRowBox);
|
||||||
|
~TableRowFlow::from_node_and_box(node, box_) as ~Flow
|
||||||
|
},
|
||||||
|
TableRowFlowClass => {
|
||||||
|
let box_ = Box::new_anonymous_table_box(node, TableCellBox);
|
||||||
|
~TableCellFlow::from_node_and_box(node, box_) as ~Flow
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
fail!("no need to generate a missing child")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns true if this flow has no children.
|
/// Returns true if this flow has no children.
|
||||||
fn is_leaf(self) -> bool {
|
fn is_leaf(self) -> bool {
|
||||||
base(self).children.len() == 0
|
base(self).children.len() == 0
|
||||||
|
|
46
src/test/html/anonymous_table.html
Normal file
46
src/test/html/anonymous_table.html
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Fixed Table</title>
|
||||||
|
<style>
|
||||||
|
.table {
|
||||||
|
display: table;
|
||||||
|
table-layout: fixed;
|
||||||
|
width: 600px;
|
||||||
|
border: solid black 2px;
|
||||||
|
}
|
||||||
|
.colgroup {
|
||||||
|
display: table-column-group;
|
||||||
|
}
|
||||||
|
.column {
|
||||||
|
display: table-column;
|
||||||
|
}
|
||||||
|
.row {
|
||||||
|
display: table-row;
|
||||||
|
}
|
||||||
|
.cell {
|
||||||
|
display: table-cell;
|
||||||
|
border: solid red 1px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p> This test checks Anonymous table objects(CSS 2.1, Section 17.2.1) </p>
|
||||||
|
<p> 1. Remove irrelevant boxes</p>
|
||||||
|
<p> 2. Generate missing child wrappers: `table-row`, `table-cell` </p>
|
||||||
|
<div class="table">
|
||||||
|
<span class="column"> inline child box of table-column. NOT Shown </span>
|
||||||
|
<span class="colgroup">
|
||||||
|
<span>inline child box of table-column-group</span> NOT Shown
|
||||||
|
</span>
|
||||||
|
<span class="cell">Cell1</span>
|
||||||
|
<span class="cell">Cell2</span>
|
||||||
|
<span class="row">
|
||||||
|
2nd Row
|
||||||
|
<span>Cell4</span>
|
||||||
|
<span class="cell">Cell3</span>
|
||||||
|
Cell5
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
<html>
|
Loading…
Add table
Add a link
Reference in a new issue