diff options
author | Junyoung Cho <jun0cho@gmail.com> | 2014-03-23 18:08:17 +0900 |
---|---|---|
committer | Junyoung Cho <june0.cho@samsung.com> | 2014-03-24 16:14:31 +0900 |
commit | 48b36e5b3ae0ccd0cbb2d146e02c3efdf58f5858 (patch) | |
tree | a990690ae14ce443d4110added66f8aae82d1577 | |
parent | 008be170d4b2c01e60baeb5827c189f4c7ed0041 (diff) | |
download | servo-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_.rs | 29 | ||||
-rw-r--r-- | src/components/main/layout/construct.rs | 117 | ||||
-rw-r--r-- | src/components/main/layout/flow.rs | 36 | ||||
-rw-r--r-- | src/test/html/anonymous_table.html | 46 |
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> |