aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/main/layout/construct.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/main/layout/construct.rs')
-rw-r--r--src/components/main/layout/construct.rs349
1 files changed, 307 insertions, 42 deletions
diff --git a/src/components/main/layout/construct.rs b/src/components/main/layout/construct.rs
index 8ae7c06a2d1..8b25937c624 100644
--- a/src/components/main/layout/construct.rs
+++ b/src/components/main/layout/construct.rs
@@ -22,15 +22,23 @@
use css::node_style::StyledNode;
use layout::block::BlockFlow;
-use layout::box_::{Box, GenericBox, IframeBox, IframeBoxInfo, ImageBox, ImageBoxInfo};
+use layout::box_::{Box, GenericBox, IframeBox, IframeBoxInfo, ImageBox, ImageBoxInfo, TableBox};
+use layout::box_::{TableCellBox, TableColumnBox, TableColumnBoxInfo, TableRowBox, TableWrapperBox};
use layout::box_::{InlineInfo, InlineParentInfo, SpecificBoxInfo, UnscannedTextBox};
use layout::box_::{UnscannedTextBoxInfo};
use layout::context::LayoutContext;
use layout::floats::FloatKind;
-use layout::flow::{Flow, MutableOwnedFlowUtils};
+use layout::flow::{Flow, ImmutableFlowUtils, MutableOwnedFlowUtils};
use layout::flow::{Descendants, AbsDescendants, FixedDescendants};
use layout::flow_list::{Rawlink};
use layout::inline::InlineFlow;
+use layout::table_wrapper::TableWrapperFlow;
+use layout::table::TableFlow;
+use layout::table_caption::TableCaptionFlow;
+use layout::table_colgroup::TableColGroupFlow;
+use layout::table_rowgroup::TableRowGroupFlow;
+use layout::table_row::TableRowFlow;
+use layout::table_cell::TableCellFlow;
use layout::text::TextRunScanner;
use layout::util::{LayoutDataAccess, OpaqueNode};
use layout::wrapper::{PostorderNodeMutTraversal, TLayoutNode, ThreadSafeLayoutNode};
@@ -39,6 +47,9 @@ use gfx::font_context::FontContext;
use script::dom::bindings::codegen::InheritTypes::TextCast;
use script::dom::bindings::js::JS;
use script::dom::element::{HTMLIFrameElementTypeId, HTMLImageElementTypeId, HTMLObjectElementTypeId};
+use script::dom::element::{HTMLTableElementTypeId, HTMLTableSectionElementTypeId};
+use script::dom::element::{HTMLTableDataCellElementTypeId, HTMLTableHeaderCellElementTypeId};
+use script::dom::element::{HTMLTableColElementTypeId, HTMLTableRowElementTypeId};
use script::dom::node::{CommentNodeTypeId, DoctypeNodeTypeId, DocumentFragmentNodeTypeId};
use script::dom::node::{DocumentNodeTypeId, ElementNodeTypeId, ProcessingInstructionNodeTypeId};
use script::dom::node::{TextNodeTypeId};
@@ -89,6 +100,8 @@ enum ConstructionItem {
InlineBoxesConstructionItem(InlineBoxesConstructionResult),
/// Potentially ignorable whitespace.
WhitespaceConstructionItem(OpaqueNode, Arc<ComputedValues>),
+ /// TableColumn Box
+ TableColumnBoxConstructionItem(Box),
}
impl ConstructionItem {
@@ -102,6 +115,7 @@ impl ConstructionItem {
}
}
WhitespaceConstructionItem(..) => {}
+ TableColumnBoxConstructionItem(_) => {}
}
}
}
@@ -288,20 +302,28 @@ impl<'a> FlowConstructor<'a> {
let data = node.get_object_data(&self.layout_context.url);
self.build_box_info_for_image(node, data)
}
+ ElementNodeTypeId(HTMLTableElementTypeId) => TableWrapperBox,
+ ElementNodeTypeId(HTMLTableColElementTypeId) => TableColumnBox(TableColumnBoxInfo::new(node)),
+ ElementNodeTypeId(HTMLTableDataCellElementTypeId) |
+ ElementNodeTypeId(HTMLTableHeaderCellElementTypeId) => TableCellBox,
+ ElementNodeTypeId(HTMLTableRowElementTypeId) |
+ ElementNodeTypeId(HTMLTableSectionElementTypeId) => TableRowBox,
TextNodeTypeId => UnscannedTextBox(UnscannedTextBoxInfo::new(node)),
_ => GenericBox,
}
}
- /// 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
}
@@ -310,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)
}
}
@@ -332,12 +359,13 @@ impl<'a> FlowConstructor<'a> {
/// this block flow.
/// Also, deal with the absolute and fixed descendants bubbled up by
/// children nodes.
- fn build_block_flow_using_children(&mut self,
- mut flow: ~Flow,
- node: &ThreadSafeLayoutNode)
- -> ConstructionResult {
+ fn build_flow_using_children(&mut self,
+ mut flow: ~Flow,
+ node: &ThreadSafeLayoutNode)
+ -> 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();
@@ -346,25 +374,38 @@ impl<'a> FlowConstructor<'a> {
match kid.swap_out_construction_result() {
NoConstructionResult => {}
FlowConstructionResult(kid_flow, kid_abs_descendants, kid_fixed_descendants) => {
- // Strip ignorable whitespace from the start of this flow per CSS 2.1 §
- // 9.2.1.1.
- if first_box {
- strip_ignorable_whitespace_from_start(&mut opt_boxes_for_inline_flow);
- first_box = false
- }
+ // 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,
+ 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.
+ if flow.is_table_kind() || first_box {
+ strip_ignorable_whitespace_from_start(&mut opt_boxes_for_inline_flow);
+ first_box = false
+ }
- // Flush any inline boxes that we were gathering up. This allows us to handle
- // {ib} splits.
- 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);
- flow.add_new_child(kid_flow);
+ // Flush any inline boxes that we were gathering up. This allows us to handle
+ // {ib} splits.
+ 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_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);
-
}
ConstructionItemConstructionResult(InlineBoxesConstructionItem(
InlineBoxesConstructionResult {
@@ -399,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)
+ }
}
}
}
@@ -419,15 +465,23 @@ impl<'a> FlowConstructor<'a> {
ConstructionItemConstructionResult(WhitespaceConstructionItem(..)) => {
// Nothing to do here.
}
+ ConstructionItemConstructionResult(TableColumnBoxConstructionItem(_)) => {
+ // TODO: Implement anonymous table objects for missing parents
+ // CSS 2.1 § 17.2.1, step 3-2
+ }
}
}
// 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);
@@ -456,7 +510,7 @@ impl<'a> FlowConstructor<'a> {
/// to happen.
fn build_flow_for_block(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult {
let flow = ~BlockFlow::from_node(self, node) as ~Flow;
- self.build_block_flow_using_children(flow, node)
+ self.build_flow_using_children(flow, node)
}
/// Builds the flow for a node with `float: {left|right}`. This yields a float `BlockFlow` with
@@ -464,7 +518,7 @@ impl<'a> FlowConstructor<'a> {
fn build_flow_for_floated_block(&mut self, node: &ThreadSafeLayoutNode, float_kind: FloatKind)
-> ConstructionResult {
let flow = ~BlockFlow::float_from_node(self, node, float_kind) as ~Flow;
- self.build_block_flow_using_children(flow, node)
+ self.build_flow_using_children(flow, node)
}
@@ -536,6 +590,10 @@ impl<'a> FlowConstructor<'a> {
whitespace_style,
UnscannedTextBox(UnscannedTextBoxInfo::from_text(~" "))))
}
+ ConstructionItemConstructionResult(TableColumnBoxConstructionItem(_)) => {
+ // TODO: Implement anonymous table objects for missing parents
+ // CSS 2.1 § 17.2.1, step 3-2
+ }
}
}
@@ -613,7 +671,7 @@ impl<'a> FlowConstructor<'a> {
let boxes_len = boxes.len();
parent_box.compute_borders(parent_box.style());
- for (i,box_) in boxes.iter().enumerate() {
+ for (i, box_) in boxes.iter().enumerate() {
if box_.inline_info.with( |data| data.is_none() ) {
box_.inline_info.set(Some(InlineInfo::new()));
}
@@ -684,6 +742,170 @@ impl<'a> FlowConstructor<'a> {
self.build_boxes_for_replaced_inline_content(node)
}
}
+
+ /// TableCaptionFlow is populated underneath TableWrapperFlow
+ fn place_table_caption_under_table_wrapper(&mut self,
+ table_wrapper_flow: &mut ~Flow,
+ node: &ThreadSafeLayoutNode) {
+ for kid in node.children() {
+ match kid.swap_out_construction_result() {
+ NoConstructionResult | ConstructionItemConstructionResult(_) => {}
+ 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);
+ }
+ }
+ }
+ }
+
+ /// 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
+ /// other `TableCaptionFlow`s or `TableFlow`s underneath it.
+ fn build_flow_for_table_wrapper(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult {
+ let box_ = Box::new_from_specific_info(node, TableWrapperBox);
+ let mut wrapper_flow = ~TableWrapperFlow::from_node_and_box(node, box_) as ~Flow;
+
+ let table_box_ = Box::new_from_specific_info(node, TableBox);
+ let table_flow = ~TableFlow::from_node_and_box(node, table_box_) as ~Flow;
+
+ // We first populate the TableFlow with other flows than TableCaptionFlow.
+ // 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);
+ 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
+ match construction_result {
+ FlowConstructionResult(table_flow, table_abs_descendants, table_fixed_descendants) => {
+ wrapper_flow.add_new_child(table_flow);
+ abs_descendants.push_descendants(table_abs_descendants);
+ fixed_descendants.push_descendants(table_fixed_descendants);
+ }
+ _ => {}
+ }
+
+ // The flow is done.
+ wrapper_flow.finish(self.layout_context);
+ let is_positioned = wrapper_flow.as_block().is_positioned();
+ let is_fixed_positioned = wrapper_flow.as_block().is_fixed();
+ let is_absolutely_positioned = wrapper_flow.as_block().is_absolutely_positioned();
+ if is_positioned {
+ // This is the CB for all the absolute descendants.
+ wrapper_flow.set_abs_descendants(abs_descendants);
+ abs_descendants = Descendants::new();
+
+ if is_fixed_positioned {
+ // Send itself along with the other fixed descendants.
+ fixed_descendants.push(Rawlink::some(wrapper_flow));
+ } else if is_absolutely_positioned {
+ // This is now the only absolute flow in the subtree which hasn't yet
+ // reached its CB.
+ abs_descendants.push(Rawlink::some(wrapper_flow));
+ }
+ }
+ FlowConstructionResult(wrapper_flow, abs_descendants, fixed_descendants)
+ }
+
+ /// Builds a flow for a node with `display: table-caption`. This yields a `TableCaptionFlow`
+ /// with possibly other `BlockFlow`s or `InlineFlow`s underneath it.
+ fn build_flow_for_table_caption(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult {
+ let flow = ~TableCaptionFlow::from_node(self, node) as ~Flow;
+ self.build_flow_using_children(flow, node)
+ }
+
+ /// Builds a flow for a node with `display: table-row-group`. This yields a `TableRowGroupFlow`
+ /// with possibly other `TableRowFlow`s underneath it.
+ fn build_flow_for_table_rowgroup(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult {
+ let box_ = Box::new_from_specific_info(node, TableRowBox);
+ let flow = ~TableRowGroupFlow::from_node_and_box(node, box_) as ~Flow;
+ self.build_flow_using_children(flow, node)
+ }
+
+ /// Builds a flow for a node with `display: table-row`. This yields a `TableRowFlow` with
+ /// possibly other `TableCellFlow`s underneath it.
+ fn build_flow_for_table_row(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult {
+ let box_ = Box::new_from_specific_info(node, TableRowBox);
+ let flow = ~TableRowFlow::from_node_and_box(node, box_) as ~Flow;
+ self.build_flow_using_children(flow, node)
+ }
+
+ /// Builds a flow for a node with `display: table-cell`. This yields a `TableCellFlow` with
+ /// possibly other `BlockFlow`s or `InlineFlow`s underneath it.
+ fn build_flow_for_table_cell(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult {
+ let box_ = Box::new_from_specific_info(node, TableCellBox);
+ let flow = ~TableCellFlow::from_node_and_box(node, box_) as ~Flow;
+ self.build_flow_using_children(flow, node)
+ }
+
+ /// Creates a box for a node with `display: table-column`.
+ fn build_boxes_for_table_column(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult {
+ // CSS 2.1 § 17.2.1. Treat all child boxes of a `table-column` as `display: none`.
+ for kid in node.children() {
+ kid.set_flow_construction_result(NoConstructionResult)
+ }
+
+ let specific = TableColumnBox(TableColumnBoxInfo::new(node));
+ let construction_item = TableColumnBoxConstructionItem(
+ Box::new_from_specific_info(node, specific)
+ );
+ ConstructionItemConstructionResult(construction_item)
+ }
+
+ /// Builds a flow for a node with `display: table-column-group`.
+ /// This yields a `TableColGroupFlow`.
+ fn build_flow_for_table_colgroup(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult {
+ let box_ = Box::new_from_specific_info(node,
+ TableColumnBox(TableColumnBoxInfo::new(node)));
+ let mut col_boxes = ~[];
+ for kid in node.children() {
+ // CSS 2.1 § 17.2.1. Treat all non-column child boxes of `table-column-group`
+ // as `display: none`.
+ match kid.swap_out_construction_result() {
+ ConstructionItemConstructionResult(TableColumnBoxConstructionItem(box_)) => {
+ col_boxes.push(box_);
+ }
+ _ => {}
+ }
+ }
+ if col_boxes.is_empty() {
+ debug!("add TableColumnBox for empty colgroup");
+ let specific = TableColumnBox(TableColumnBoxInfo::new(node));
+ col_boxes.push( Box::new_from_specific_info(node, specific) );
+ }
+ let mut flow = ~TableColGroupFlow::from_node_and_boxes(node, box_, col_boxes) as ~Flow;
+ flow.finish(self.layout_context);
+
+ FlowConstructionResult(flow, Descendants::new(), Descendants::new())
+ }
}
impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> {
@@ -725,6 +947,12 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> {
}
}
+ // Table items contribute table flow construction results.
+ (display::table, _, _) => {
+ let construction_result = self.build_flow_for_table_wrapper(node);
+ node.set_flow_construction_result(construction_result)
+ }
+
// Absolutely positioned elements will have computed value of
// `float` as 'none' and `display` as per the table.
// Currently, for original `display` value of 'inline', the new
@@ -739,6 +967,43 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> {
node.set_flow_construction_result(construction_result)
}
+ // Table items contribute table flow construction results.
+ (display::table_caption, _, _) => {
+ let construction_result = self.build_flow_for_table_caption(node);
+ node.set_flow_construction_result(construction_result)
+ }
+
+ // Table items contribute table flow construction results.
+ (display::table_column_group, _, _) => {
+ let construction_result = self.build_flow_for_table_colgroup(node);
+ node.set_flow_construction_result(construction_result)
+ }
+
+ // Table items contribute table flow construction results.
+ (display::table_column, _, _) => {
+ let construction_result = self.build_boxes_for_table_column(node);
+ node.set_flow_construction_result(construction_result)
+ }
+
+ // Table items contribute table flow construction results.
+ (display::table_row_group, _, _) | (display::table_header_group, _, _) |
+ (display::table_footer_group, _, _) => {
+ let construction_result = self.build_flow_for_table_rowgroup(node);
+ node.set_flow_construction_result(construction_result)
+ }
+
+ // Table items contribute table flow construction results.
+ (display::table_row, _, _) => {
+ let construction_result = self.build_flow_for_table_row(node);
+ node.set_flow_construction_result(construction_result)
+ }
+
+ // Table items contribute table flow construction results.
+ (display::table_cell, _, _) => {
+ let construction_result = self.build_flow_for_table_cell(node);
+ node.set_flow_construction_result(construction_result)
+ }
+
// Block flows that are not floated contribute block flow construction results.
//
// TODO(pcwalton): Make this only trigger for blocks and handle the other `display`