mirror of
https://github.com/servo/servo.git
synced 2025-08-05 21:50:18 +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::cell::RefCell;
|
||||
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::{border_style, clear, font_family, line_height, position};
|
||||
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.
|
||||
pub fn from_opaque_node_and_style(node: OpaqueNode,
|
||||
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
|
||||
/// otherwise.
|
||||
#[inline(always)]
|
||||
fn flush_inline_boxes_to_flow(&mut self,
|
||||
boxes: ~[Box],
|
||||
flow: &mut ~Flow,
|
||||
node: &ThreadSafeLayoutNode) {
|
||||
fn flush_inline_boxes_to_flow_or_list(&mut self,
|
||||
boxes: ~[Box],
|
||||
flow: &mut ~Flow,
|
||||
flow_list: &mut ~[~Flow],
|
||||
node: &ThreadSafeLayoutNode) {
|
||||
if boxes.len() == 0 {
|
||||
return
|
||||
}
|
||||
|
@ -330,18 +332,23 @@ impl<'a> FlowConstructor<'a> {
|
|||
TextRunScanner::new().scan_for_runs(self.font_context(), inline_flow);
|
||||
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
|
||||
/// the given flow.
|
||||
fn flush_inline_boxes_to_flow_if_necessary(&mut self,
|
||||
opt_boxes: &mut Option<~[Box]>,
|
||||
flow: &mut ~Flow,
|
||||
node: &ThreadSafeLayoutNode) {
|
||||
/// the given flow or pushes it onto the given flow list.
|
||||
fn flush_inline_boxes_to_flow_or_list_if_necessary(&mut self,
|
||||
opt_boxes: &mut Option<~[Box]>,
|
||||
flow: &mut ~Flow,
|
||||
flow_list: &mut ~[~Flow],
|
||||
node: &ThreadSafeLayoutNode) {
|
||||
let opt_boxes = mem::replace(opt_boxes, None);
|
||||
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 {
|
||||
// Gather up boxes for the inline flows we might need to create.
|
||||
let mut opt_boxes_for_inline_flow = None;
|
||||
let mut consecutive_siblings = ~[];
|
||||
let mut first_box = true;
|
||||
// List of absolute descendants, in tree order.
|
||||
let mut abs_descendants = Descendants::new();
|
||||
|
@ -368,7 +376,11 @@ impl<'a> FlowConstructor<'a> {
|
|||
FlowConstructionResult(kid_flow, kid_abs_descendants, kid_fixed_descendants) => {
|
||||
// If kid_flow is TableCaptionFlow, kid_flow should be added under TableWrapperFlow.
|
||||
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 {
|
||||
// Strip ignorable whitespace from the start of this flow per CSS 2.1 §
|
||||
// 9.2.1.1.
|
||||
|
@ -382,13 +394,18 @@ impl<'a> FlowConstructor<'a> {
|
|||
debug!("flushing {} inline box(es) to flow A",
|
||||
opt_boxes_for_inline_flow.as_ref()
|
||||
.map_or(0, |boxes| boxes.len()));
|
||||
self.flush_inline_boxes_to_flow_if_necessary(&mut opt_boxes_for_inline_flow,
|
||||
&mut flow,
|
||||
node);
|
||||
self.flush_inline_boxes_to_flow_or_list_if_necessary(&mut opt_boxes_for_inline_flow,
|
||||
&mut flow,
|
||||
&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);
|
||||
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(
|
||||
InlineBoxesConstructionResult {
|
||||
|
@ -423,14 +440,19 @@ impl<'a> FlowConstructor<'a> {
|
|||
opt_boxes_for_inline_flow.as_ref()
|
||||
.map_or(0,
|
||||
|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 flow,
|
||||
&mut consecutive_siblings,
|
||||
node);
|
||||
|
||||
// Push the flow generated by the {ib} split onto our list of
|
||||
// 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}
|
||||
// splits, after stripping ignorable whitespace.
|
||||
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,
|
||||
&mut flow,
|
||||
node);
|
||||
self.flush_inline_boxes_to_flow_or_list_if_necessary(&mut opt_boxes_for_inline_flow,
|
||||
&mut flow,
|
||||
&mut consecutive_siblings,
|
||||
node);
|
||||
if !consecutive_siblings.is_empty() {
|
||||
self.generate_anonymous_missing_child(consecutive_siblings, &mut flow, node);
|
||||
}
|
||||
|
||||
// The flow is done.
|
||||
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,
|
||||
node: &ThreadSafeLayoutNode)
|
||||
-> (Descendants, Descendants) {
|
||||
// List of absolute descendants, in tree order.
|
||||
let mut abs_descendants = Descendants::new();
|
||||
let mut fixed_descendants = Descendants::new();
|
||||
node: &ThreadSafeLayoutNode) {
|
||||
for kid in node.children() {
|
||||
match kid.swap_out_construction_result() {
|
||||
NoConstructionResult | ConstructionItemConstructionResult(_) => {}
|
||||
FlowConstructionResult(kid_flow, kid_abs_descendants, kid_fixed_descendants) => {
|
||||
FlowConstructionResult(kid_flow, _, _) => {
|
||||
// Only kid flows with table-caption are matched here.
|
||||
assert!(kid_flow.is_table_caption());
|
||||
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
|
||||
|
@ -752,8 +797,10 @@ impl<'a> FlowConstructor<'a> {
|
|||
// We then populate the TableWrapperFlow with TableCaptionFlow, and attach
|
||||
// the TableFlow to the TableWrapperFlow
|
||||
let construction_result = self.build_flow_using_children(table_flow, node);
|
||||
let (mut abs_descendants, mut fixed_descendants)
|
||||
= self.build_table_wrapper_flow_using_children(&mut wrapper_flow, node);
|
||||
self.place_table_caption_under_table_wrapper(&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.
|
||||
// All caption blocks are placed before the table flow
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
use css::node_style::StyledNode;
|
||||
use layout::block::{BlockFlow};
|
||||
use layout::box_::Box;
|
||||
use layout::box_::{Box, TableRowBox, TableCellBox};
|
||||
use layout::context::LayoutContext;
|
||||
use layout::construct::OptVector;
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
fn is_leaf(self) -> bool {
|
||||
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