aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJunyoung Cho <jun0cho@gmail.com>2014-03-23 18:08:17 +0900
committerJunyoung Cho <june0.cho@samsung.com>2014-03-24 16:14:31 +0900
commit48b36e5b3ae0ccd0cbb2d146e02c3efdf58f5858 (patch)
treea990690ae14ce443d4110added66f8aae82d1577
parent008be170d4b2c01e60baeb5827c189f4c7ed0041 (diff)
downloadservo-48b36e5b3ae0ccd0cbb2d146e02c3efdf58f5858.tar.gz
servo-48b36e5b3ae0ccd0cbb2d146e02c3efdf58f5858.zip
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
-rw-r--r--src/components/main/layout/box_.rs29
-rw-r--r--src/components/main/layout/construct.rs117
-rw-r--r--src/components/main/layout/flow.rs36
-rw-r--r--src/test/html/anonymous_table.html46
4 files changed, 191 insertions, 37 deletions
diff --git a/src/components/main/layout/box_.rs b/src/components/main/layout/box_.rs
index 7cf067f5423..972ac79da9a 100644
--- a/src/components/main/layout/box_.rs
+++ b/src/components/main/layout/box_.rs
@@ -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>,
diff --git a/src/components/main/layout/construct.rs b/src/components/main/layout/construct.rs
index 857b7c82530..8b25937c624 100644
--- a/src/components/main/layout/construct.rs
+++ b/src/components/main/layout/construct.rs
@@ -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
diff --git a/src/components/main/layout/flow.rs b/src/components/main/layout/flow.rs
index cc4db0cb350..099f79c8e81 100644
--- a/src/components/main/layout/flow.rs
+++ b/src/components/main/layout/flow.rs
@@ -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
diff --git a/src/test/html/anonymous_table.html b/src/test/html/anonymous_table.html
new file mode 100644
index 00000000000..90363d43d28
--- /dev/null
+++ b/src/test/html/anonymous_table.html
@@ -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>