layout: Support multiple boxes per node; don't store fixed/absolute

descendant links separately
This commit is contained in:
Patrick Walton 2014-03-28 13:19:58 -07:00
parent 4fd950eae0
commit 98bf325e76
4 changed files with 84 additions and 102 deletions

View file

@ -503,7 +503,7 @@ impl BlockFlow {
-> BlockFlow {
BlockFlow {
base: BaseFlow::new((*node).clone()),
box_: Some(Box::new(constructor, node)),
box_: Some(Box::new(constructor, node, MainBoxKind)),
is_root: false,
static_y_offset: Au::new(0),
float: None
@ -528,7 +528,7 @@ impl BlockFlow {
-> BlockFlow {
BlockFlow {
base: BaseFlow::new((*node).clone()),
box_: Some(Box::new(constructor, node)),
box_: Some(Box::new(constructor, node, MainBoxKind)),
is_root: false,
static_y_offset: Au::new(0),
float: Some(~FloatedBlockInfo::new(float_kind))

View file

@ -22,14 +22,14 @@
use css::node_style::StyledNode;
use layout::block::BlockFlow;
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::box_::{Box, GenericBox, IframeBox, IframeBoxInfo, ImageBox, ImageBoxInfo};
use layout::box_::{InlineInfo, InlineParentInfo, MainBoxKind, SpecificBoxInfo, SubBoxKind};
use layout::box_::{TableBox, TableCellBox, TableColumnBox, TableColumnBoxInfo, TableRowBox};
use layout::box_::{TableWrapperBox, UnscannedTextBox, UnscannedTextBoxInfo};
use layout::context::LayoutContext;
use layout::floats::FloatKind;
use layout::flow::{Flow, ImmutableFlowUtils, MutableOwnedFlowUtils};
use layout::flow::{Descendants, AbsDescendants, FixedDescendants};
use layout::flow::{Descendants, AbsDescendants};
use layout::flow_list::{Rawlink};
use layout::inline::InlineFlow;
use layout::table_wrapper::TableWrapperFlow;
@ -40,31 +40,34 @@ 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::util::{LayoutDataAccess, OpaqueNodeMethods};
use layout::wrapper::{PostorderNodeMutTraversal, TLayoutNode, ThreadSafeLayoutNode};
use layout::wrapper::{Before, BeforeBlock, After, AfterBlock, Normal};
use extra::url::Url;
use gfx::display_list::OpaqueNode;
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::element::{HTMLIFrameElementTypeId, HTMLImageElementTypeId};
use script::dom::element::{HTMLObjectElementTypeId, HTMLPseudoElementTypeId};
use script::dom::element::{HTMLTableColElementTypeId, HTMLTableDataCellElementTypeId};
use script::dom::element::{HTMLTableElementTypeId, HTMLTableHeaderCellElementTypeId};
use script::dom::element::{HTMLTableRowElementTypeId, HTMLTableSectionElementTypeId};
use script::dom::node::{CommentNodeTypeId, DoctypeNodeTypeId, DocumentFragmentNodeTypeId};
use script::dom::node::{DocumentNodeTypeId, ElementNodeTypeId, ProcessingInstructionNodeTypeId};
use script::dom::node::{TextNodeTypeId};
use script::dom::text::Text;
use style::computed_values::{display, position, float, white_space};
use style::ComputedValues;
use servo_util::geometry::Au;
use servo_util::namespace;
use servo_util::url::parse_url;
use servo_util::url::is_image_data;
use servo_util::smallvec::SmallVec;
use servo_util::str::is_whitespace;
use extra::url::Url;
use sync::Arc;
use servo_util::url::{is_image_data, parse_url};
use std::mem;
use std::num::Zero;
use style::ComputedValues;
use style::computed_values::{display, position, float, white_space};
use sync::Arc;
/// The results of flow construction for a DOM node.
pub enum ConstructionResult {
@ -75,7 +78,7 @@ pub enum ConstructionResult {
/// This node contributed a flow at the proper position in the tree.
/// Nothing more needs to be done for this node. It has bubbled up fixed
/// and absolute descendant flows that have a CB above it.
FlowConstructionResult(~Flow, AbsDescendants, FixedDescendants),
FlowConstructionResult(~Flow, AbsDescendants),
/// This node contributed some object or objects that will be needed to construct a proper flow
/// later up the tree, but these objects have not yet found their home.
@ -86,7 +89,7 @@ impl ConstructionResult {
fn destroy(&mut self) {
match *self {
NoConstructionResult => {}
FlowConstructionResult(ref mut flow, _, _) => flow.destroy(),
FlowConstructionResult(ref mut flow, _) => flow.destroy(),
ConstructionItemConstructionResult(ref mut item) => item.destroy(),
}
}
@ -132,9 +135,6 @@ struct InlineBoxesConstructionResult {
/// Any absolute descendants that we're bubbling up.
abs_descendants: AbsDescendants,
/// Any fixed descendants that we're bubbling up.
fixed_descendants: FixedDescendants,
}
/// Represents an {ib} split that has not yet found the containing block that it belongs to. This
@ -493,16 +493,13 @@ impl<'a> FlowConstructor<'a> {
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(flow));
} else if is_absolutely_positioned {
if is_fixed_positioned || 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(flow));
}
}
FlowConstructionResult(flow, abs_descendants, fixed_descendants)
FlowConstructionResult(flow, abs_descendants)
}
/// Builds a flow for a node with `display: block`. This yields a `BlockFlow` with possibly
@ -521,7 +518,6 @@ impl<'a> FlowConstructor<'a> {
self.build_flow_using_children(flow, node)
}
/// Concatenates the boxes of kids, adding in our own borders/padding/margins if necessary.
/// Returns the `InlineBoxesConstructionResult`, if any. There will be no
/// `InlineBoxesConstructionResult` if this node consisted entirely of ignorable whitespace.
@ -530,13 +526,12 @@ impl<'a> FlowConstructor<'a> {
let mut opt_inline_block_splits = None;
let mut opt_box_accumulator = None;
let mut abs_descendants = Descendants::new();
let mut fixed_descendants = Descendants::new();
// Concatenate all the boxes of our kids, creating {ib} splits as necessary.
for kid in node.children() {
match kid.swap_out_construction_result() {
NoConstructionResult => {}
FlowConstructionResult(flow, kid_abs_descendants, kid_fixed_descendants) => {
FlowConstructionResult(flow, kid_abs_descendants) => {
// {ib} split. Flush the accumulator to our new split and make a new
// accumulator to hold any subsequent boxes we come across.
let split = InlineBlockSplit {
@ -545,14 +540,12 @@ impl<'a> FlowConstructor<'a> {
};
opt_inline_block_splits.push(split);
abs_descendants.push_descendants(kid_abs_descendants);
fixed_descendants.push_descendants(kid_fixed_descendants);
}
ConstructionItemConstructionResult(InlineBoxesConstructionItem(
InlineBoxesConstructionResult {
splits: opt_splits,
boxes: boxes,
abs_descendants: kid_abs_descendants,
fixed_descendants: kid_fixed_descendants,
})) => {
// Bubble up {ib} splits.
@ -579,7 +572,6 @@ impl<'a> FlowConstructor<'a> {
// Push residual boxes.
opt_box_accumulator.push_all_move(boxes);
abs_descendants.push_descendants(kid_abs_descendants);
fixed_descendants.push_descendants(kid_fixed_descendants);
}
ConstructionItemConstructionResult(WhitespaceConstructionItem(whitespace_node,
whitespace_style))
@ -612,7 +604,7 @@ impl<'a> FlowConstructor<'a> {
for box_ in boxes.iter() {
total.push(box_);
}
self.set_inline_info_for_inline_child(&total, node);
self.set_inline_info_for_inline_child(total, node);
},
None => {
@ -622,7 +614,7 @@ impl<'a> FlowConstructor<'a> {
total.push(box_);
}
}
self.set_inline_info_for_inline_child(&total, node);
self.set_inline_info_for_inline_child(total, node);
}
}
},
@ -633,7 +625,7 @@ impl<'a> FlowConstructor<'a> {
for box_ in boxes.iter() {
total.push(box_);
}
self.set_inline_info_for_inline_child(&total, node);
self.set_inline_info_for_inline_child(total, node);
},
None => {}
}
@ -648,7 +640,6 @@ impl<'a> FlowConstructor<'a> {
splits: opt_inline_block_splits,
boxes: opt_box_accumulator.to_vec(),
abs_descendants: abs_descendants,
fixed_descendants: fixed_descendants,
});
ConstructionItemConstructionResult(construction_item)
} else {
@ -656,10 +647,11 @@ impl<'a> FlowConstructor<'a> {
}
}
// FIXME(pcwalton): Why does this function create a box only to throw it away???
fn set_inline_info_for_inline_child(&mut self,
boxes: &~[&Box],
boxes: &[&Box],
parent_node: &ThreadSafeLayoutNode) {
let parent_box = Box::new(self, parent_node);
let parent_box = Box::new(self, parent_node, MainBoxKind);
let font_style = parent_box.font_style();
let font_group = self.font_context().get_resolved_font_for_style(&font_style);
let (font_ascent,font_descent) = font_group.borrow().with_mut( |fg| {
@ -671,33 +663,38 @@ impl<'a> FlowConstructor<'a> {
let boxes_len = boxes.len();
parent_box.compute_borders(parent_box.style());
// FIXME(pcwalton): I suspect that `Au(0)` is not correct for the containing block width.
parent_box.compute_padding(parent_box.style(), Au(0));
for (i, box_) in boxes.iter().enumerate() {
if box_.inline_info.with( |data| data.is_none() ) {
box_.inline_info.set(Some(InlineInfo::new()));
}
let mut border = parent_box.border.get();
let mut padding = parent_box.padding.get();
if i != 0 {
border.left = Zero::zero();
padding.left = Zero::zero()
}
if i != (boxes_len - 1) {
border.right = Zero::zero();
padding.right = Zero::zero()
}
let mut info = box_.inline_info.borrow_mut();
match info.get() {
&Some(ref mut info) => {
// TODO(ksh8281) compute margin,padding
info.parent_info.push(
InlineParentInfo {
padding: Zero::zero(),
border: border,
margin: Zero::zero(),
style: parent_box.style.clone(),
font_ascent: font_ascent,
font_descent: font_descent,
node: OpaqueNode::from_thread_safe_layout_node(parent_node),
});
// TODO(ksh8281): Compute margins.
info.parent_info.push(InlineParentInfo {
padding: padding,
border: border,
margin: Zero::zero(),
style: parent_box.style.clone(),
font_ascent: font_ascent,
font_descent: font_descent,
node: OpaqueNodeMethods::from_thread_safe_layout_node(parent_node),
})
},
&None => {}
}
@ -712,20 +709,22 @@ impl<'a> FlowConstructor<'a> {
}
// If this node is ignorable whitespace, bail out now.
//
// FIXME(pcwalton): Don't do this if there's padding or borders.
if node.is_ignorable_whitespace() {
let opaque_node = OpaqueNode::from_thread_safe_layout_node(node);
let opaque_node = OpaqueNodeMethods::from_thread_safe_layout_node(node);
return ConstructionItemConstructionResult(WhitespaceConstructionItem(
opaque_node,
node.style().clone()))
}
let mut opt_box_accumulator = None;
opt_box_accumulator.push(Box::new(self, node, MainBoxKind));
let construction_item = InlineBoxesConstructionItem(InlineBoxesConstructionResult {
splits: None,
boxes: ~[
Box::new(self, node)
],
boxes: opt_box_accumulator.to_vec(),
abs_descendants: Descendants::new(),
fixed_descendants: Descendants::new(),
});
ConstructionItemConstructionResult(construction_item)
}
@ -750,7 +749,7 @@ impl<'a> FlowConstructor<'a> {
for kid in node.children() {
match kid.swap_out_construction_result() {
NoConstructionResult | ConstructionItemConstructionResult(_) => {}
FlowConstructionResult(kid_flow, _, _) => {
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);
@ -761,8 +760,10 @@ impl<'a> FlowConstructor<'a> {
/// 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) {
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() {
@ -805,10 +806,9 @@ impl<'a> FlowConstructor<'a> {
// 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) => {
FlowConstructionResult(table_flow, table_abs_descendants) => {
wrapper_flow.add_new_child(table_flow);
abs_descendants.push_descendants(table_abs_descendants);
fixed_descendants.push_descendants(table_fixed_descendants);
}
_ => {}
}
@ -832,7 +832,7 @@ impl<'a> FlowConstructor<'a> {
abs_descendants.push(Rawlink::some(wrapper_flow));
}
}
FlowConstructionResult(wrapper_flow, abs_descendants, fixed_descendants)
FlowConstructionResult(wrapper_flow, abs_descendants)
}
/// Builds a flow for a node with `display: table-caption`. This yields a `TableCaptionFlow`
@ -904,7 +904,7 @@ impl<'a> FlowConstructor<'a> {
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())
FlowConstructionResult(flow, Descendants::new())
}
}
@ -922,6 +922,10 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> {
fn process(&mut self, node: &ThreadSafeLayoutNode) -> bool {
// Get the `display` property for this node, and determine whether this node is floated.
let (display, float, positioning) = match node.type_id() {
ElementNodeTypeId(HTMLPseudoElementTypeId) => {
let style = node.style().get();
(display::inline, style.Box.get().float, style.Box.get().position)
}
ElementNodeTypeId(_) => {
let style = node.style().get();
(style.Box.get().display, style.Box.get().float, style.Box.get().position)
@ -1050,6 +1054,7 @@ impl<'ln> NodeUtils for ThreadSafeLayoutNode<'ln> {
DoctypeNodeTypeId |
DocumentFragmentNodeTypeId |
DocumentNodeTypeId |
ElementNodeTypeId(HTMLPseudoElementTypeId) |
ElementNodeTypeId(HTMLImageElementTypeId) => true,
ElementNodeTypeId(HTMLObjectElementTypeId) => self.has_object_data(),
ElementNodeTypeId(_) => false,

View file

@ -209,6 +209,11 @@ pub trait Flow {
false
}
/// Returns true if this is an absolute containing block.
fn is_absolute_containing_block(&self) -> bool {
false
}
/// Return the dimensions of the CB generated _by_ this flow for absolute descendants.
fn generated_cb_size(&self) -> Size2D<Au> {
fail!("generated_cb_size not yet implemented")
@ -372,11 +377,6 @@ pub trait MutableOwnedFlowUtils {
/// Set this flow as the Containing Block for all the absolute descendants.
fn set_abs_descendants(&mut self, abs_descendants: AbsDescendants);
/// Set fixed descendants for this flow.
///
/// Set yourself as the Containing Block for all the fixed descendants.
fn set_fixed_descendants(&mut self, fixed_descendants: AbsDescendants);
/// Destroys the flow.
fn destroy(&mut self);
}
@ -704,7 +704,6 @@ impl Descendants {
}
pub type AbsDescendants = Descendants;
pub type FixedDescendants = Descendants;
type DescendantIter<'a> = MutItems<'a, Rawlink>;
@ -749,15 +748,15 @@ pub struct BaseFlow {
/// decide whether to do an in-order traversal for assign_height.
num_floats: uint,
/// The collapsible margins for this flow, if any.
collapsible_margins: CollapsibleMargins,
/// The position of this flow in page coordinates, computed during display list construction.
abs_position: Point2D<Au>,
/// Details about descendants with position 'absolute' for which we are
/// the CB. This is in tree order. This includes any direct children.
/// Details about descendants with position 'absolute' or 'fixed' for which we are the
/// containing block. This is in tree order. This includes any direct children.
abs_descendants: AbsDescendants,
/// Details about descendants with position 'fixed'.
/// TODO: Optimize this, because this will be set only for the root.
fixed_descendants: FixedDescendants,
/// Offset wrt the nearest positioned ancestor - aka the Containing Block
/// for any absolutely positioned elements.
@ -822,9 +821,10 @@ impl BaseFlow {
floats: Floats::new(),
num_floats: 0,
collapsible_margins: CollapsibleMargins::new(),
clear: clear::none,
abs_position: Point2D(Au::new(0), Au::new(0)),
abs_descendants: Descendants::new(),
fixed_descendants: Descendants::new(),
absolute_static_x_offset: Au::new(0),
fixed_static_x_offset: Au::new(0),
absolute_cb: Rawlink::none(),
@ -1081,6 +1081,7 @@ impl<'a> MutableFlowUtils for &'a mut Flow {
overflow = overflow.union(&kid_overflow)
}
// FIXME(pcwalton): This is wrong for `position: fixed`.
for descendant_link in mut_base(self).abs_descendants.iter() {
match descendant_link.resolve() {
Some(flow) => {
@ -1327,32 +1328,10 @@ impl MutableOwnedFlowUtils for ~Flow {
}
}
/// Set fixed descendants for this flow.
///
/// Set yourself as the Containing Block for all the fixed descendants.
///
/// Assumption: This is called in a bottom-up traversal, so that nothing
/// else is accessing the descendant flows.
/// Assumption: This is the root flow.
fn set_fixed_descendants(&mut self, fixed_descendants: FixedDescendants) {
let self_link = Rawlink::some(*self);
let block = self.as_block();
block.base.fixed_descendants = fixed_descendants;
for descendant_link in block.base.fixed_descendants.iter() {
match descendant_link.resolve() {
Some(flow) => {
let base = mut_base(flow);
base.absolute_cb = self_link.clone();
}
None => fail!("empty Rawlink to a descendant")
}
}
}
/// Destroys the flow.
fn destroy(&mut self) {
let self_borrowed: &mut Flow = *self;
self_borrowed.destroy();
}
}

View file

@ -434,15 +434,13 @@ impl LayoutTask {
None => fail!("no layout data for root node"),
};
let mut flow = match result {
FlowConstructionResult(mut flow, abs_descendants, fixed_descendants) => {
FlowConstructionResult(mut flow, abs_descendants) => {
// Note: Assuming that the root has display 'static' (as per
// CSS Section 9.3.1). Otherwise, if it were absolutely
// positioned, it would return a reference to itself in
// `abs_descendants` and would lead to a circular reference.
// Set Root as CB for any remaining absolute descendants.
flow.set_abs_descendants(abs_descendants);
// Set Root as CB for all fixed descendants.
flow.set_fixed_descendants(fixed_descendants);
flow
}
_ => fail!("Flow construction didn't result in a flow at the root of the tree!"),