servo: Merge #1681 - Implement `position: absolute` for non-replaced elements (from pradeep90:absolute-position); r=pcwalton

+ Re-implement fixed positioning using the absolute positioning code.
+ Add reftests for absolute positioning and fixed positioning.
+ Refactor assign_widths in BlockFlow to isolate the calculation of
widths and margins.
+ Pass down details of the Containing Block for absolute and fixed flows
during layout. Use it to calculate the static position of absolute flows.
+ Defer calculation of absolute flow dimensions till we build the
display list.

This implements https://github.com/mozilla/servo/issues/1537 and https://github.com/mozilla/servo/issues/787

Source-Repo: https://github.com/servo/servo
Source-Revision: ada9224d0ef5b403562ebaaeb2e5f66729ffc589
This commit is contained in:
S Pradeep Kumar 2014-03-03 13:37:33 -05:00
Родитель 340dcb6f0d
Коммит 49b6ce84d3
8 изменённых файлов: 1531 добавлений и 260 удалений

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -605,6 +605,22 @@ impl Box {
specified(padding, content_box_width)
}
pub fn padding_box_size(&self) -> Size2D<Au> {
let border_box_size = self.border_box.get().size;
Size2D(border_box_size.width - self.border.get().left - self.border.get().right,
border_box_size.height - self.border.get().top - self.border.get().bottom)
}
pub fn border_and_padding_horiz(&self) -> Au {
self.border.get().left + self.border.get().right + self.padding.get().left
+ self.padding.get().right
}
pub fn border_and_padding_vert(&self) -> Au {
self.border.get().top + self.border.get().bottom + self.padding.get().top
+ self.padding.get().bottom
}
pub fn noncontent_width(&self) -> Au {
self.noncontent_left() + self.noncontent_right()
}
@ -613,6 +629,7 @@ impl Box {
self.noncontent_top() + self.noncontent_bottom()
}
// Return offset from original position because of `position: relative`.
pub fn relative_position(&self, container_block_size: &Size2D<Au>) -> Point2D<Au> {
fn left_right(style: &ComputedValues, block_width: Au) -> Au {
// TODO(ksh8281) : consider RTL(right-to-left) culture
@ -651,6 +668,7 @@ impl Box {
rel_pos.y = rel_pos.y + top_bottom(self.style(), container_block_size.height);
}
// Go over the ancestor boxes and add all relative offsets (if any).
let info = self.inline_info.borrow();
match info.get() {
&Some(ref info) => {
@ -977,7 +995,7 @@ impl Box {
/// Arguments:
/// * `builder`: The display list builder, which manages the coordinate system and options.
/// * `dirty`: The dirty rectangle in the coordinate system of the owning flow.
/// * `origin`: The total offset from the display list root flow to the owning flow of this
/// * `flow_origin`: Position of the origin of the owning flow wrt the display list root flow.
/// box.
/// * `list`: The display list to which items should be appended.
///
@ -990,15 +1008,16 @@ impl Box {
&self,
builder: &DisplayListBuilder,
dirty: &Rect<Au>,
offset: Point2D<Au>,
flow_origin: Point2D<Au>,
flow: &Flow,
index: uint,
lists: &RefCell<DisplayListCollection<E>>) {
// Box position wrt to the owning flow.
let box_bounds = self.border_box.get();
let absolute_box_bounds = box_bounds.translate(&offset);
let absolute_box_bounds = box_bounds.translate(&flow_origin);
debug!("Box::build_display_list at rel={}, abs={}: {:s}",
box_bounds, absolute_box_bounds, self.debug_str());
debug!("Box::build_display_list: dirty={}, offset={}", *dirty, offset);
debug!("Box::build_display_list: dirty={}, flow_origin={}", *dirty, flow_origin);
if self.style().InheritedBox.get().visibility != visibility::visible {
return;
@ -1011,10 +1030,15 @@ impl Box {
return;
}
self.paint_inline_background_border_if_applicable(index, lists, &absolute_box_bounds, &offset);
self.paint_inline_background_border_if_applicable(index, lists, &absolute_box_bounds, &flow_origin);
// Add the background to the list, if applicable.
self.paint_background_if_applicable(builder, index, lists, &absolute_box_bounds);
// Add a border, if applicable.
//
// TODO: Outlines.
self.paint_borders_if_applicable(index, lists, &absolute_box_bounds);
match self.specific {
UnscannedTextBox(_) => fail!("Shouldn't see unscanned boxes here."),
ScannedTextBox(ref text_box) => {
@ -1122,6 +1146,7 @@ impl Box {
// FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We
// should have a real `SERVO_DEBUG` system.
debug!("{:?}", {
// This prints a debug border around the border of this box.
let debug_border = SideOffsets2D::new_all_same(Au::from_px(1));
lists.with_mut(|lists| {
@ -1208,15 +1233,11 @@ impl Box {
// iframe is actually going to be displayed.
match self.specific {
IframeBox(ref iframe_box) => {
self.finalize_position_and_size_of_iframe(iframe_box, offset, builder.ctx)
self.finalize_position_and_size_of_iframe(iframe_box, flow_origin, builder.ctx)
}
GenericBox | ImageBox(_) | ScannedTextBox(_) | UnscannedTextBox(_) => {}
}
// Add a border, if applicable.
//
// TODO: Outlines.
self.paint_borders_if_applicable(index, lists, &absolute_box_bounds);
}
/// Returns the *minimum width* and *preferred width* of this box as defined by CSS 2.1.
@ -1432,8 +1453,10 @@ impl Box {
}
}
/// Assigns the appropriate width to this box.
pub fn assign_width(&self,container_width: Au) {
/// Assigns replaced width for this box only if it is replaced content.
///
/// CSS 2.1 § 10.3.2.
pub fn assign_replaced_width_if_necessary(&self,container_width: Au) {
match self.specific {
GenericBox | IframeBox(_) => {
}
@ -1469,7 +1492,8 @@ impl Box {
image_box_info.computed_width.set(Some(width));
}
ScannedTextBox(_) => {
// Scanned text boxes will have already had their content_widths assigned by this point.
// Scanned text boxes will have already had their
// content_widths assigned by this point.
let mut position = self.border_box.borrow_mut();
position.get().size.width = position.get().size.width + self.noncontent_width() +
self.noncontent_inline_left() + self.noncontent_inline_right();
@ -1478,6 +1502,7 @@ impl Box {
}
}
/// Assign height for image and scanned text boxes.
pub fn assign_height(&self) {
match self.specific {
GenericBox | IframeBox(_) => {
@ -1514,6 +1539,8 @@ impl Box {
ScannedTextBox(_) => {
// Scanned text boxes will have already had their widths assigned by this point
let mut position = self.border_box.borrow_mut();
// Scanned text boxes' content heights are calculated by the
// text run scanner during Flow construction.
position.get().size.height
= position.get().size.height + self.noncontent_height()
}

Просмотреть файл

@ -28,6 +28,8 @@ use layout::box_::{UnscannedTextBoxInfo};
use layout::context::LayoutContext;
use layout::floats::FloatKind;
use layout::flow::{Flow, MutableOwnedFlowUtils};
use layout::flow::{Descendants, AbsDescendants, FixedDescendants};
use layout::flow_list::{Rawlink};
use layout::inline::InlineFlow;
use layout::text::TextRunScanner;
use layout::util::{LayoutDataAccess, OpaqueNode};
@ -59,9 +61,10 @@ pub enum ConstructionResult {
/// created nodes have their `ConstructionResult` set to.
NoConstructionResult,
/// This node contributed a flow at the proper position in the tree. Nothing more needs to be
/// done for this node.
FlowConstructionResult(~Flow),
/// 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),
/// 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.
@ -72,7 +75,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(),
}
}
@ -112,6 +115,12 @@ struct InlineBoxesConstructionResult {
/// Any boxes that succeed the {ib} splits.
boxes: ~[Box],
/// 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
@ -155,7 +164,7 @@ impl InlineBlockSplit {
/// Methods on optional vectors.
///
/// TODO(pcwalton): I think this will no longer be necessary once Rust #8981 lands.
trait OptVector<T> {
pub trait OptVector<T> {
/// Turns this optional vector into an owned one. If the optional vector is `None`, then this
/// simply returns an empty owned vector.
fn to_vec(self) -> ~[T];
@ -316,17 +325,27 @@ impl<'a> FlowConstructor<'a> {
}
}
/// Builds the children flows underneath a node with `display: block`. After this call,
/// other `BlockFlow`s or `InlineFlow`s will be populated underneath this node, depending on
/// whether {ib} splits needed to happen.
fn build_children_of_block_flow(&mut self, flow: &mut ~Flow, node: &ThreadSafeLayoutNode) {
/// Build block flow for current node using information from children nodes.
///
/// Consume results from children and combine them, handling {ib} splits.
/// Block flows and inline flows thus created will become the children of
/// 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 {
// Gather up boxes for the inline flows we might need to create.
let mut opt_boxes_for_inline_flow = None;
let mut first_box = true;
// List of absolute descendants, in tree order.
let mut abs_descendants = Descendants::new();
let mut fixed_descendants = Descendants::new();
for kid in node.children() {
match kid.swap_out_construction_result() {
NoConstructionResult => {}
FlowConstructionResult(kid_flow) => {
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 {
@ -340,14 +359,19 @@ impl<'a> FlowConstructor<'a> {
opt_boxes_for_inline_flow.as_ref()
.map_default(0, |boxes| boxes.len()));
self.flush_inline_boxes_to_flow_if_necessary(&mut opt_boxes_for_inline_flow,
flow,
&mut flow,
node);
flow.add_new_child(kid_flow)
flow.add_new_child(kid_flow);
abs_descendants.push_descendants(kid_abs_descendants);
fixed_descendants.push_descendants(kid_fixed_descendants);
}
ConstructionItemConstructionResult(InlineBoxesConstructionItem(
InlineBoxesConstructionResult {
splits: opt_splits,
boxes: boxes
boxes: boxes,
abs_descendants: kid_abs_descendants,
fixed_descendants: kid_fixed_descendants,
})) => {
// Add any {ib} splits.
match opt_splits {
@ -377,7 +401,7 @@ impl<'a> FlowConstructor<'a> {
|boxes| boxes.len()));
self.flush_inline_boxes_to_flow_if_necessary(
&mut opt_boxes_for_inline_flow,
flow,
&mut flow,
node);
// Push the flow generated by the {ib} split onto our list of
@ -388,7 +412,9 @@ impl<'a> FlowConstructor<'a> {
}
// Add the boxes to the list we're maintaining.
opt_boxes_for_inline_flow.push_all_move(boxes)
opt_boxes_for_inline_flow.push_all_move(boxes);
abs_descendants.push_descendants(kid_abs_descendants);
fixed_descendants.push_descendants(kid_fixed_descendants);
}
ConstructionItemConstructionResult(WhitespaceConstructionItem(..)) => {
// Nothing to do here.
@ -400,29 +426,45 @@ impl<'a> FlowConstructor<'a> {
// 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,
flow,
&mut flow,
node);
// The flow is done. If it ended up with no kids, add the flow to the leaf set.
flow.finish(self.layout_context)
// The flow is done.
flow.finish(self.layout_context);
let is_positioned = flow.as_block().is_positioned();
let is_fixed_positioned = flow.as_block().is_fixed();
let is_absolutely_positioned = flow.as_block().is_absolutely_positioned();
if is_positioned {
// This is the CB for all the absolute descendants.
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 {
// 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)
}
/// Builds a flow for a node with `display: block`. This yields a `BlockFlow` with possibly
/// other `BlockFlow`s or `InlineFlow`s underneath it, depending on whether {ib} splits needed
/// to happen.
fn build_flow_for_block(&mut self, node: &ThreadSafeLayoutNode, is_fixed: bool) -> ~Flow {
let mut flow = ~BlockFlow::from_node(self, node, is_fixed) as ~Flow;
self.build_children_of_block_flow(&mut flow, node);
flow
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)
}
/// Builds the flow for a node with `float: {left|right}`. This yields a float `BlockFlow` with
/// a `BlockFlow` underneath it.
fn build_flow_for_floated_block(&mut self, node: &ThreadSafeLayoutNode, float_kind: FloatKind)
-> ~Flow {
let mut flow = ~BlockFlow::float_from_node(self, node, float_kind) as ~Flow;
self.build_children_of_block_flow(&mut flow, node);
flow
-> ConstructionResult {
let flow = ~BlockFlow::float_from_node(self, node, float_kind) as ~Flow;
self.build_block_flow_using_children(flow, node)
}
@ -433,24 +475,30 @@ impl<'a> FlowConstructor<'a> {
-> ConstructionResult {
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) => {
FlowConstructionResult(flow, kid_abs_descendants, kid_fixed_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 {
predecessor_boxes: util::replace(&mut opt_box_accumulator, None).to_vec(),
flow: flow,
};
opt_inline_block_splits.push(split)
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
boxes: boxes,
abs_descendants: kid_abs_descendants,
fixed_descendants: kid_fixed_descendants,
})) => {
// Bubble up {ib} splits.
@ -475,7 +523,9 @@ impl<'a> FlowConstructor<'a> {
}
// Push residual boxes.
opt_box_accumulator.push_all_move(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))
@ -533,10 +583,14 @@ impl<'a> FlowConstructor<'a> {
}
// Finally, make a new construction result.
if opt_inline_block_splits.len() > 0 || opt_box_accumulator.len() > 0 {
if opt_inline_block_splits.len() > 0 || opt_box_accumulator.len() > 0
|| abs_descendants.len() > 0 {
let construction_item = InlineBoxesConstructionItem(InlineBoxesConstructionResult {
splits: opt_inline_block_splits,
boxes: opt_box_accumulator.to_vec(),
abs_descendants: abs_descendants,
fixed_descendants: fixed_descendants,
});
ConstructionItemConstructionResult(construction_item)
} else {
@ -612,6 +666,8 @@ impl<'a> FlowConstructor<'a> {
boxes: ~[
Box::new(self, node)
],
abs_descendants: Descendants::new(),
fixed_descendants: Descendants::new(),
});
ConstructionItemConstructionResult(construction_item)
}
@ -636,7 +692,7 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> {
#[inline(always)]
fn process(&mut self, node: &ThreadSafeLayoutNode) -> bool {
// Get the `display` property for this node, and determine whether this node is floated.
let (display, float, position) = match node.type_id() {
let (display, float, positioning) = match node.type_id() {
ElementNodeTypeId(_) => {
let style = node.style().get();
(style.Box.get().display, style.Box.get().float, style.Box.get().position)
@ -652,7 +708,7 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> {
debug!("building flow for node: {:?} {:?}", display, float);
// Switch on display and floatedness.
match (display, float, position) {
match (display, float, positioning) {
// `display: none` contributes no flow construction result. Nuke the flow construction
// results of children.
(display::none, _, _) => {
@ -662,6 +718,10 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> {
}
}
(_, _, position::absolute) | (_, _, position::fixed) => {
node.set_flow_construction_result(self.build_flow_for_block(node))
}
// Inline items contribute inline box construction results.
(display::inline, float::none, _) => {
let construction_result = self.build_boxes_for_inline(node);
@ -673,20 +733,15 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> {
// TODO(pcwalton): Make this only trigger for blocks and handle the other `display`
// properties separately.
(_, _, position::fixed) => {
let flow = self.build_flow_for_block(node, true);
node.set_flow_construction_result(FlowConstructionResult(flow))
}
(_, float::none, _) => {
let flow = self.build_flow_for_block(node, false);
node.set_flow_construction_result(FlowConstructionResult(flow))
node.set_flow_construction_result(self.build_flow_for_block(node))
}
// Floated flows contribute float flow construction results.
(_, float_value, _) => {
let float_kind = FloatKind::from_property(float_value);
let flow = self.build_flow_for_floated_block(node, float_kind);
node.set_flow_construction_result(FlowConstructionResult(flow))
node.set_flow_construction_result(
self.build_flow_for_floated_block(node, float_kind))
}
}
@ -779,7 +834,7 @@ trait ObjectElement {
/// Returns true if this node has object data that is correct uri.
fn has_object_data(&self) -> bool;
/// Returns the "data" attribute value parsed as a URL
/// Returns the "data" attribute value parsed as a URL
fn get_object_data(&self, base_url: &Url) -> Option<Url>;
}
@ -793,7 +848,7 @@ impl<'ln> ObjectElement for ThreadSafeLayoutNode<'ln> {
match self.get_type_and_data() {
(None, Some(uri)) => is_image_data(uri),
_ => false
}
}
}
fn get_object_data(&self, base_url: &Url) -> Option<Url> {
@ -854,4 +909,3 @@ fn strip_ignorable_whitespace_from_end(opt_boxes: &mut Option<~[Box]>) {
*opt_boxes = None
}
}

Просмотреть файл

@ -16,7 +16,7 @@
///
/// * `BlockFlow`: A flow that establishes a block context. It has several child flows, each of
/// which are positioned according to block formatting context rules (CSS block boxes). Block
/// flows also contain a single `GenericBox` to represent their rendered borders, padding, etc.
/// flows also contain a single box to represent their rendered borders, padding, etc.
/// The BlockFlow at the root of the tree has special behavior: it stretches to the boundaries of
/// the viewport.
///
@ -26,9 +26,10 @@
/// similar methods.
use css::node_style::StyledNode;
use layout::block::BlockFlow;
use layout::block::{BlockFlow};
use layout::box_::Box;
use layout::context::LayoutContext;
use layout::construct::OptVector;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
use layout::floats::Floats;
use layout::incremental::RestyleDamage;
@ -45,12 +46,15 @@ use geom::rect::Rect;
use gfx::display_list::{ClipDisplayItemClass, DisplayListCollection, DisplayList};
use layout::display_list_builder::ToGfxColor;
use gfx::color::Color;
use servo_util::smallvec::{SmallVec, SmallVec0};
use servo_util::geometry::Au;
use std::cast;
use std::cell::RefCell;
use std::sync::atomics::Relaxed;
use std::vec::VecMutIterator;
use std::iter::Zip;
use style::ComputedValues;
use style::computed_values::text_align;
use style::computed_values::{text_align, position};
/// Virtual methods that make up a float context.
///
@ -116,6 +120,63 @@ pub trait Flow {
/// Marks this flow as the root flow. The default implementation is a no-op.
fn mark_as_root(&mut self) {}
// Note that the following functions are mostly called using static method
// dispatch, so it's ok to have them in this trait. Plus, they have
// different behaviour for different types of Flow, so they can't go into
// the Immutable / Mutable Flow Utils traits without additional casts.
/// Return true if store overflow is delayed for this flow.
///
/// Currently happens only for absolutely positioned flows.
fn is_store_overflow_delayed(&mut self) -> bool {
false
}
fn is_root(&self) -> bool {
false
}
fn is_float(&self) -> bool {
false
}
/// The 'position' property of this flow.
fn positioning(&self) -> position::T {
position::static_
}
/// Return true if this flow has position 'fixed'.
fn is_fixed(&self) -> bool {
self.positioning() == position::fixed
}
fn is_positioned(&self) -> bool {
self.is_relatively_positioned() || self.is_absolutely_positioned()
}
fn is_relatively_positioned(&self) -> bool {
self.positioning() == position::relative
}
fn is_absolutely_positioned(&self) -> bool {
self.positioning() == position::absolute || self.is_fixed()
}
/// Return true if this is the root of an Absolute flow tree.
fn is_root_of_absolute_flow_tree(&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")
}
/// Return position of the CB generated by this flow from the start of this flow.
fn generated_cb_position(&self) -> Point2D<Au> {
fail!("this is not the CB-generating flow you're looking for")
}
/// Returns a debugging string describing this flow.
fn debug_str(&self) -> ~str {
~"???"
@ -208,6 +269,7 @@ pub trait MutableFlowUtils {
self,
builder: &DisplayListBuilder,
container_block_size: &Size2D<Au>,
absolute_cb_abs_position: Point2D<Au>,
dirty: &Rect<Au>,
index: uint,
mut list: &RefCell<DisplayListCollection<E>>)
@ -230,6 +292,16 @@ pub trait MutableOwnedFlowUtils {
/// properly computed. (This is not, however, a memory safety problem.)
fn finish(&mut self, context: &mut LayoutContext);
/// Set absolute descendants for this flow.
///
/// 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);
}
@ -483,6 +555,61 @@ impl FlowFlags {
}
}
/// The Descendants of a flow.
///
/// Also, details about their position wrt this flow.
/// FIXME: This should use @pcwalton's reference counting scheme (Coming Soon).
pub struct Descendants {
/// Links to every Descendant.
descendant_links: SmallVec0<Rawlink>,
/// Static y offsets of all descendants from the start of this flow box.
static_y_offsets: SmallVec0<Au>,
}
impl Descendants {
pub fn new() -> Descendants {
Descendants {
descendant_links: SmallVec0::new(),
static_y_offsets: SmallVec0::new(),
}
}
pub fn len(&self) -> uint {
self.descendant_links.len()
}
pub fn push(&mut self, given_descendant: Rawlink) {
self.descendant_links.push(given_descendant);
}
/// Push the given descendants on to the existing descendants.
///
/// Ignore any static y offsets, because they are None before layout.
pub fn push_descendants(&mut self, mut given_descendants: Descendants) {
for elem in given_descendants.descendant_links.move_iter() {
self.descendant_links.push(elem);
}
}
/// Return an iterator over the descendant flows.
pub fn iter<'a>(&'a mut self) -> DescendantIter<'a> {
self.descendant_links.mut_slice_from(0).mut_iter()
}
/// Return an iterator over (descendant, static y offset).
pub fn iter_with_offset<'a>(&'a mut self) -> DescendantOffsetIter<'a> {
self.descendant_links.mut_slice_from(0).mut_iter().zip(
self.static_y_offsets.mut_slice_from(0).mut_iter())
}
}
pub type AbsDescendants = Descendants;
pub type FixedDescendants = Descendants;
type DescendantIter<'a> = VecMutIterator<'a, Rawlink>;
type DescendantOffsetIter<'a> = Zip<VecMutIterator<'a, Rawlink>, VecMutIterator<'a, Au>>;
/// Data common to all flows.
pub struct BaseFlow {
restyle_damage: RestyleDamage,
@ -498,8 +625,9 @@ pub struct BaseFlow {
min_width: Au,
pref_width: Au,
/// The position of the upper left corner of the border box of this flow, relative to the
/// containing block.
/// The upper left corner of the box representing this flow, relative to
/// the box representing its parent flow.
/// For absolute flows, this represents the position wrt to its Containing Block.
position: Rect<Au>,
/// The amount of overflow of this flow, relative to the containing block. Must include all the
@ -514,12 +642,32 @@ pub struct BaseFlow {
/// The floats next to this flow.
floats: Floats,
/// The number of floated descendants of this flow (including this flow, if it's floated).
/// For normal flows, this is the number of floated descendants that are
/// not contained within any other floated descendant of this flow. For
/// floats, it is 1.
/// It is used to allocate float data if necessary and to
/// decide whether to do an in-order traversal for assign_height.
num_floats: uint,
/// 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.
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.
absolute_static_x_offset: Au,
/// Offset wrt the Initial Containing Block.
fixed_static_x_offset: Au,
/// Reference to the Containing Block, if this flow is absolutely positioned.
absolute_cb: Rawlink,
/// Whether this flow has been destroyed.
///
/// TODO(pcwalton): Pack this into the flags? Need to be careful because manipulation of this
@ -576,6 +724,11 @@ impl BaseFlow {
floats: Floats::new(),
num_floats: 0,
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(),
destroyed: false,
@ -707,15 +860,56 @@ impl<'a> MutableFlowUtils for &'a mut Flow {
f(mut_base(self).children.back_mut())
}
/// Calculate and set overflow for current flow.
///
/// CSS Section 11.1
/// This is the union of rectangles of the flows for which we define the
/// Containing Block.
///
/// Assumption: This is called in a bottom-up traversal, so kids' overflows have
/// already been set.
/// Assumption: Absolute descendants have had their overflow calculated.
fn store_overflow(self, _: &mut LayoutContext) {
let my_position = mut_base(self).position;
let mut overflow = my_position;
for kid in mut_base(self).child_iter() {
let mut kid_overflow = base(kid).overflow;
kid_overflow = kid_overflow.translate(&my_position.origin);
overflow = overflow.union(&kid_overflow)
if self.is_block_container() {
for kid in child_iter(self) {
if kid.is_store_overflow_delayed() {
// Absolute flows will be handled by their CB. If we are
// their CB, they will show up in `abs_descendants`.
continue;
}
let mut kid_overflow = base(kid).overflow;
kid_overflow = kid_overflow.translate(&my_position.origin);
overflow = overflow.union(&kid_overflow)
}
for descendant_link in mut_base(self).abs_descendants.iter() {
match descendant_link.resolve() {
Some(flow) => {
let mut kid_overflow = base(flow).overflow;
kid_overflow = kid_overflow.translate(&my_position.origin);
overflow = overflow.union(&kid_overflow)
}
None => fail!("empty Rawlink to a descendant")
}
}
if self.is_root() {
for fixed_descendant_link in mut_base(self).fixed_descendants.iter() {
match fixed_descendant_link.resolve() {
Some(flow) => {
let mut kid_overflow = base(flow).overflow;
kid_overflow = kid_overflow.translate(&my_position.origin);
overflow = overflow.union(&kid_overflow)
}
None => fail!("empty Rawlink to a descendant")
}
}
}
}
mut_base(self).overflow = overflow
mut_base(self).overflow = overflow;
}
/// Push display items for current flow and its children onto `list`.
@ -723,17 +917,29 @@ impl<'a> MutableFlowUtils for &'a mut Flow {
/// For InlineFlow, add display items for all its boxes onto list`.
/// For BlockFlow, add a ClipDisplayItemClass for itself and its children,
/// plus any other display items like border.
///
/// `container_block_size`: Size of the Containing Block for the current
/// flow. This is used for relative positioning (which resolves percentage
/// values for 'top', etc. after all Containing Block heights have been computed.)
/// `absolute_cb_abs_position`: Absolute position of the Containing Block
/// for the flow if it is absolutely positioned.
fn build_display_lists<E:ExtraDisplayListData>(
self,
builder: &DisplayListBuilder,
container_block_size: &Size2D<Au>,
absolute_cb_abs_position: Point2D<Au>,
dirty: &Rect<Au>,
mut index: uint,
lists: &RefCell<DisplayListCollection<E>>)
-> bool {
debug!("Flow: building display list");
index = match self.class() {
BlockFlowClass => self.as_block().build_display_list_block(builder, container_block_size, dirty, index, lists),
BlockFlowClass => self.as_block().build_display_list_block(builder,
container_block_size,
absolute_cb_abs_position,
dirty,
index,
lists),
InlineFlowClass => self.as_inline().build_display_list_inline(builder, container_block_size, dirty, index, lists),
};
@ -742,24 +948,68 @@ impl<'a> MutableFlowUtils for &'a mut Flow {
}
if self.is_block_container() {
let block = self.as_block();
let mut child_lists = DisplayListCollection::new();
child_lists.add_list(DisplayList::new());
let child_lists = RefCell::new(child_lists);
let container_block_size = match self.class() {
BlockFlowClass => {
if self.as_block().box_.is_some() {
self.as_block().box_.get_ref().border_box.get().size
} else {
base(self).position.size
}
},
_ => {
base(self).position.size
}
};
let container_block_size;
let abs_cb_position;
// TODO(pradeep): Move this into a generated CB function and stuff in Flow.
match block.box_ {
Some(ref box_) => {
// FIXME: This should be the size of the content box (which is the
// Containing Block formed by a BlockFlow), not the border box.
container_block_size = box_.border_box.get().size;
for kid in child_iter(self) {
kid.build_display_lists(builder, &container_block_size, dirty, 0u, &child_lists);
abs_cb_position = if block.is_positioned() {
block.base.abs_position + block.generated_cb_position()
} else {
absolute_cb_abs_position
};
}
None => fail!("Flow: block container should have a box_")
}
for kid in block.base.child_iter() {
if kid.is_absolutely_positioned() {
// All absolute flows will be handled by their CB.
continue;
}
kid.build_display_lists(builder, &container_block_size,
abs_cb_position,
dirty, 0u, &child_lists);
}
// TODO: Maybe we should handle position 'absolute' and 'fixed'
// descendants before normal descendants just in case there is a
// problem when display-list building is parallel and both the
// original parent and this flow access the same absolute flow.
// Note that this can only be done once we have paint order
// working cos currently the later boxes paint over the absolute
// and fixed boxes :|
for abs_descendant_link in block.base.abs_descendants.iter() {
match abs_descendant_link.resolve() {
Some(flow) => {
// TODO(pradeep): Send in your abs_position directly.
flow.build_display_lists(builder, &container_block_size,
abs_cb_position,
dirty, 0u, &child_lists);
}
None => fail!("empty Rawlink to a descendant")
}
}
if block.is_root() {
for fixed_descendant_link in block.base.fixed_descendants.iter() {
match fixed_descendant_link.resolve() {
Some(flow) => {
flow.build_display_lists(builder, &container_block_size,
abs_cb_position,
dirty, 0u, &child_lists);
}
None => fail!("empty Rawlink to a descendant")
}
}
}
let mut child_lists = Some(child_lists.unwrap());
@ -822,10 +1072,54 @@ impl MutableOwnedFlowUtils for ~Flow {
}
}
/// Set absolute descendants for this flow.
///
/// Set yourself as the Containing Block for all the absolute descendants.
///
/// Assumption: This is called in a bottom-up traversal, so that nothing
/// else is accessing the descendant flows.
fn set_abs_descendants(&mut self, abs_descendants: AbsDescendants) {
let self_link = Rawlink::some(*self);
let block = self.as_block();
block.base.abs_descendants = abs_descendants;
for descendant_link in block.base.abs_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")
}
}
}
/// 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();
}
}

Просмотреть файл

@ -19,6 +19,10 @@ pub struct Rawlink {
priv obj: *mut (),
}
/// Doubly-linked list of Flows.
///
/// The forward links are strong references.
/// The backward links are weak references.
pub struct FlowList {
priv length: uint,
priv list_head: Link,
@ -51,7 +55,7 @@ impl Rawlink {
}
/// Like Option::Some for Rawlink
fn some(n: &mut Flow) -> Rawlink {
pub fn some(n: &mut Flow) -> Rawlink {
unsafe { cast::transmute(n) }
}
@ -65,7 +69,7 @@ impl Rawlink {
}
}
fn resolve(&mut self) -> Option<&mut Flow> {
pub fn resolve(&mut self) -> Option<&mut Flow> {
if self.obj.is_null() {
None
} else {

Просмотреть файл

@ -657,18 +657,13 @@ impl Flow for InlineFlow {
{
let this = &mut *self;
for box_ in this.boxes.iter() {
box_.assign_width(self.base.position.size.width);
box_.assign_replaced_width_if_necessary(self.base.position.size.width);
}
}
// FIXME(ksh8281) avoid copy
let flags_info = self.base.flags_info.clone();
for kid in self.base.child_iter() {
let child_base = flow::mut_base(kid);
child_base.position.size.width = self.base.position.size.width;
child_base.flags_info.flags.set_inorder(self.base.flags_info.flags.inorder());
child_base.flags_info.propagate_text_alignment_from_parent(&flags_info)
}
assert!(self.base.children.len() == 0,
"InlineFlow: should not have children flows in the current layout implementation.");
// There are no child contexts, so stop here.
// TODO(Issue #225): once there are 'inline-block' elements, this won't be
@ -685,6 +680,9 @@ impl Flow for InlineFlow {
self.assign_height(ctx);
}
/// Calculate and set the height of this Flow.
///
/// CSS Section 10.6.1
fn assign_height(&mut self, _: &mut LayoutContext) {
debug!("assign_height_inline: assigning height for flow");
@ -901,4 +899,3 @@ impl Flow for InlineFlow {
~"InlineFlow: " + self.boxes.map(|s| s.debug_str()).connect(", ")
}
}

Просмотреть файл

@ -23,6 +23,7 @@ use layout::wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode};
use extra::url::Url;
use extra::arc::{Arc, MutexArc};
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use gfx::display_list::{ClipDisplayItemClass, DisplayItem, DisplayItemIterator};
@ -211,7 +212,11 @@ impl<'a> PostorderFlowTraversal for AssignHeightsAndStoreOverflowTraversal<'a> {
#[inline]
fn process(&mut self, flow: &mut Flow) -> bool {
flow.assign_height(self.layout_context);
flow.store_overflow(self.layout_context);
// Skip store-overflow for absolutely positioned flows. That will be
// done in a separate traversal.
if !flow.is_store_overflow_delayed() {
flow.store_overflow(self.layout_context);
}
true
}
@ -276,7 +281,7 @@ impl LayoutTask {
chan: LayoutChan,
constellation_chan: ConstellationChan,
script_chan: ScriptChan,
render_chan: RenderChan<OpaqueNode>,
render_chan: RenderChan<OpaqueNode>,
image_cache_task: ImageCacheTask,
opts: &Opts,
profiler_chan: ProfilerChan)
@ -402,7 +407,7 @@ impl LayoutTask {
/// crash.
fn exit_now(&mut self) {
let (response_port, response_chan) = Chan::new();
match self.parallel_traversal {
None => {}
Some(ref mut traversal) => traversal.shutdown(),
@ -426,7 +431,17 @@ impl LayoutTask {
None => fail!("no layout data for root node"),
};
let mut flow = match result {
FlowConstructionResult(flow) => flow,
FlowConstructionResult(mut flow, abs_descendants, fixed_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!"),
};
flow.mark_as_root();
@ -614,6 +629,7 @@ impl LayoutTask {
if data.goal == ReflowForDisplay {
profile(time::LayoutDispListBuildCategory, self.profiler_chan.clone(), || {
let root_size = flow::base(layout_root).position.size;
let root_abs_position = Point2D(Au::new(0), Au::new(0));
let mut display_list_collection = DisplayListCollection::new();
display_list_collection.add_list(DisplayList::<OpaqueNode>::new());
let display_list_collection = ~RefCell::new(display_list_collection);
@ -621,14 +637,16 @@ impl LayoutTask {
let display_list_builder = DisplayListBuilder {
ctx: &layout_ctx,
};
layout_root.build_display_lists(&display_list_builder, &root_size, &dirty, 0u, display_list_collection);
layout_root.build_display_lists(&display_list_builder, &root_size,
root_abs_position,
&dirty, 0u, display_list_collection);
let display_list_collection = Arc::new(display_list_collection.unwrap());
let mut color = color::rgba(255.0, 255.0, 255.0, 255.0);
for child in node.traverse_preorder() {
if child.type_id() == ElementNodeTypeId(HTMLHtmlElementTypeId) ||
if child.type_id() == ElementNodeTypeId(HTMLHtmlElementTypeId) ||
child.type_id() == ElementNodeTypeId(HTMLBodyElementTypeId) {
let element_bg_color = {
let thread_safe_child = ThreadSafeLayoutNode::new(&child);
@ -768,7 +786,7 @@ impl LayoutTask {
Au::from_frac_px(point.y as f64));
let resp = hit_test(x,y,display_list.list);
if resp.is_some() {
reply_chan.send(Ok(resp.unwrap()));
reply_chan.send(Ok(resp.unwrap()));
return
}
}
@ -842,4 +860,3 @@ impl LayoutTask {
util::replace(layout_data_ref.get(), None));
}
}

Просмотреть файл

@ -119,6 +119,18 @@ impl FlowParallelInfo {
/// A parallel bottom-up flow traversal.
trait ParallelPostorderFlowTraversal : PostorderFlowTraversal {
/// Process current flow and potentially traverse its ancestors.
///
/// If we are the last child that finished processing, recursively process
/// our parent. Else, stop.
/// Also, stop at the root (obviously :P).
///
/// Thus, if we start with all the leaves of a tree, we end up traversing
/// the whole tree bottom-up because each parent will be processed exactly
/// once (by the last child that finishes processing).
///
/// The only communication between siblings is that they both
/// fetch-and-subtract the parent's children count.
fn run_parallel(&mut self,
mut unsafe_flow: UnsafeFlow,
_: &mut WorkerProxy<*mut LayoutContext,PaddedUnsafeFlow>) {
@ -144,8 +156,9 @@ trait ParallelPostorderFlowTraversal : PostorderFlowTraversal {
break
}
// No, we're not at the root yet. Then are we the last sibling of our parent? If
// so, we can continue on with our parent; otherwise, we've gotta wait.
// No, we're not at the root yet. Then are we the last child
// of our parent to finish processing? If so, we can continue
// on with our parent; otherwise, we've gotta wait.
let parent: &mut ~Flow = cast::transmute(&unsafe_parent);
let parent_base = flow::mut_base(*parent);
if parent_base.parallel.children_count.fetch_sub(1, SeqCst) == 1 {
@ -426,4 +439,3 @@ pub fn traverse_flow_tree_preorder(root: &mut ~Flow,
queue.data = ptr::mut_null()
}