Implement position: absolute for non-replaced elements.

+ 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 commit is contained in:
S Pradeep Kumar 2014-01-29 14:25:39 +09:00
parent 478c9bfc57
commit c4d177a354
15 changed files with 1203 additions and 194 deletions

File diff suppressed because it is too large Load diff

View file

@ -605,6 +605,16 @@ impl Box {
specified(padding, content_box_width) specified(padding, content_box_width)
} }
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_vertical(&self) -> Au {
self.border.get().top + self.border.get().bottom + self.padding.get().top
+ self.padding.get().bottom
}
pub fn noncontent_width(&self) -> Au { pub fn noncontent_width(&self) -> Au {
self.noncontent_left() + self.noncontent_right() self.noncontent_left() + self.noncontent_right()
} }
@ -613,6 +623,7 @@ impl Box {
self.noncontent_top() + self.noncontent_bottom() 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> { pub fn relative_position(&self, container_block_size: &Size2D<Au>) -> Point2D<Au> {
fn left_right(style: &ComputedValues, block_width: Au) -> Au { fn left_right(style: &ComputedValues, block_width: Au) -> Au {
// TODO(ksh8281) : consider RTL(right-to-left) culture // TODO(ksh8281) : consider RTL(right-to-left) culture
@ -651,6 +662,7 @@ impl Box {
rel_pos.y = rel_pos.y + top_bottom(self.style(), container_block_size.height); 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(); let info = self.inline_info.borrow();
match info.get() { match info.get() {
&Some(ref info) => { &Some(ref info) => {
@ -1122,6 +1134,7 @@ impl Box {
// FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We // FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We
// should have a real `SERVO_DEBUG` system. // should have a real `SERVO_DEBUG` system.
debug!("{:?}", { debug!("{:?}", {
// This prints a debug border around the border of this box.
let debug_border = SideOffsets2D::new_all_same(Au::from_px(1)); let debug_border = SideOffsets2D::new_all_same(Au::from_px(1));
lists.with_mut(|lists| { lists.with_mut(|lists| {
@ -1432,8 +1445,10 @@ impl Box {
} }
} }
/// Assigns the appropriate width to this box. /// Assigns replaced width for this box only if it is replaced content.
pub fn assign_width(&self,container_width: Au) { ///
/// CSS 2.1 § 10.3.2.
pub fn assign_replaced_width_if_necessary(&self,container_width: Au) {
match self.specific { match self.specific {
GenericBox | IframeBox(_) => { GenericBox | IframeBox(_) => {
} }
@ -1469,7 +1484,8 @@ impl Box {
image_box_info.computed_width.set(Some(width)); image_box_info.computed_width.set(Some(width));
} }
ScannedTextBox(_) => { 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(); let mut position = self.border_box.borrow_mut();
position.get().size.width = position.get().size.width + self.noncontent_width() + position.get().size.width = position.get().size.width + self.noncontent_width() +
self.noncontent_inline_left() + self.noncontent_inline_right(); self.noncontent_inline_left() + self.noncontent_inline_right();
@ -1478,6 +1494,7 @@ impl Box {
} }
} }
/// Assign height for image and scanned text boxes.
pub fn assign_height(&self) { pub fn assign_height(&self) {
match self.specific { match self.specific {
GenericBox | IframeBox(_) => { GenericBox | IframeBox(_) => {
@ -1514,6 +1531,8 @@ impl Box {
ScannedTextBox(_) => { ScannedTextBox(_) => {
// Scanned text boxes will have already had their widths assigned by this point // Scanned text boxes will have already had their widths assigned by this point
let mut position = self.border_box.borrow_mut(); 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
= position.get().size.height + self.noncontent_height() = position.get().size.height + self.noncontent_height()
} }

View file

@ -410,8 +410,9 @@ impl<'a> FlowConstructor<'a> {
/// Builds a flow for a node with `display: block`. This yields a `BlockFlow` with possibly /// 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 /// other `BlockFlow`s or `InlineFlow`s underneath it, depending on whether {ib} splits needed
/// to happen. /// to happen.
fn build_flow_for_block(&mut self, node: &ThreadSafeLayoutNode, is_fixed: bool) -> ~Flow { fn build_flow_for_block(&mut self, node: &ThreadSafeLayoutNode, positioning: position::T)
let mut flow = ~BlockFlow::from_node(self, node, is_fixed) as ~Flow; -> ~Flow {
let mut flow = ~BlockFlow::from_node(self, node, positioning) as ~Flow;
self.build_children_of_block_flow(&mut flow, node); self.build_children_of_block_flow(&mut flow, node);
flow flow
} }
@ -433,7 +434,7 @@ impl<'a> FlowConstructor<'a> {
-> ConstructionResult { -> ConstructionResult {
let mut opt_inline_block_splits = None; let mut opt_inline_block_splits = None;
let mut opt_box_accumulator = None; let mut opt_box_accumulator = None;
// Concatenate all the boxes of our kids, creating {ib} splits as necessary. // Concatenate all the boxes of our kids, creating {ib} splits as necessary.
for kid in node.children() { for kid in node.children() {
match kid.swap_out_construction_result() { match kid.swap_out_construction_result() {
@ -636,7 +637,7 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> {
#[inline(always)] #[inline(always)]
fn process(&mut self, node: &ThreadSafeLayoutNode) -> bool { fn process(&mut self, node: &ThreadSafeLayoutNode) -> bool {
// Get the `display` property for this node, and determine whether this node is floated. // 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(_) => { ElementNodeTypeId(_) => {
let style = node.style().get(); let style = node.style().get();
(style.Box.get().display, style.Box.get().float, style.Box.get().position) (style.Box.get().display, style.Box.get().float, style.Box.get().position)
@ -652,7 +653,7 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> {
debug!("building flow for node: {:?} {:?}", display, float); debug!("building flow for node: {:?} {:?}", display, float);
// Switch on display and floatedness. // Switch on display and floatedness.
match (display, float, position) { match (display, float, positioning) {
// `display: none` contributes no flow construction result. Nuke the flow construction // `display: none` contributes no flow construction result. Nuke the flow construction
// results of children. // results of children.
(display::none, _, _) => { (display::none, _, _) => {
@ -673,12 +674,12 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> {
// TODO(pcwalton): Make this only trigger for blocks and handle the other `display` // TODO(pcwalton): Make this only trigger for blocks and handle the other `display`
// properties separately. // properties separately.
(_, _, position::fixed) => { (_, _, position::absolute) | (_, _, position::fixed) => {
let flow = self.build_flow_for_block(node, true); let flow = self.build_flow_for_block(node, positioning);
node.set_flow_construction_result(FlowConstructionResult(flow)) node.set_flow_construction_result(FlowConstructionResult(flow))
} }
(_, float::none, _) => { (_, float::none, _) => {
let flow = self.build_flow_for_block(node, false); let flow = self.build_flow_for_block(node, positioning);
node.set_flow_construction_result(FlowConstructionResult(flow)) node.set_flow_construction_result(FlowConstructionResult(flow))
} }
@ -779,7 +780,7 @@ trait ObjectElement {
/// Returns true if this node has object data that is correct uri. /// Returns true if this node has object data that is correct uri.
fn has_object_data(&self) -> bool; 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>; fn get_object_data(&self, base_url: &Url) -> Option<Url>;
} }
@ -793,7 +794,7 @@ impl<'ln> ObjectElement for ThreadSafeLayoutNode<'ln> {
match self.get_type_and_data() { match self.get_type_and_data() {
(None, Some(uri)) => is_image_data(uri), (None, Some(uri)) => is_image_data(uri),
_ => false _ => false
} }
} }
fn get_object_data(&self, base_url: &Url) -> Option<Url> { fn get_object_data(&self, base_url: &Url) -> Option<Url> {
@ -854,4 +855,3 @@ fn strip_ignorable_whitespace_from_end(opt_boxes: &mut Option<~[Box]>) {
*opt_boxes = None *opt_boxes = None
} }
} }

View file

@ -16,7 +16,7 @@
/// ///
/// * `BlockFlow`: A flow that establishes a block context. It has several child flows, each of /// * `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 /// 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 BlockFlow at the root of the tree has special behavior: it stretches to the boundaries of
/// the viewport. /// the viewport.
/// ///
@ -26,7 +26,7 @@
/// similar methods. /// similar methods.
use css::node_style::StyledNode; use css::node_style::StyledNode;
use layout::block::BlockFlow; use layout::block::{BlockFlow};
use layout::box_::Box; use layout::box_::Box;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData}; use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
@ -208,6 +208,7 @@ pub trait MutableFlowUtils {
self, self,
builder: &DisplayListBuilder, builder: &DisplayListBuilder,
container_block_size: &Size2D<Au>, container_block_size: &Size2D<Au>,
absolute_cb_abs_position: Point2D<Au>,
dirty: &Rect<Au>, dirty: &Rect<Au>,
index: uint, index: uint,
mut list: &RefCell<DisplayListCollection<E>>) mut list: &RefCell<DisplayListCollection<E>>)
@ -498,8 +499,9 @@ pub struct BaseFlow {
min_width: Au, min_width: Au,
pref_width: Au, pref_width: Au,
/// The position of the upper left corner of the border box of this flow, relative to the /// The upper left corner of the box representing this flow, relative to
/// containing block. /// the box representing its parent flow.
/// For absolute flows, this represents the position wrt to its Containing Block.
position: Rect<Au>, position: Rect<Au>,
/// The amount of overflow of this flow, relative to the containing block. Must include all the /// The amount of overflow of this flow, relative to the containing block. Must include all the
@ -514,7 +516,11 @@ pub struct BaseFlow {
/// The floats next to this flow. /// The floats next to this flow.
floats: Floats, 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, num_floats: uint,
/// The position of this flow in page coordinates, computed during display list construction. /// The position of this flow in page coordinates, computed during display list construction.
@ -707,6 +713,19 @@ impl<'a> MutableFlowUtils for &'a mut Flow {
f(mut_base(self).children.back_mut()) f(mut_base(self).children.back_mut())
} }
/// Calculate and set overflow for current flow.
///
/// CSS Section 11.1
/// This is ideally the union of all flows for which we define the
/// Containing Block.
///
/// Assumption: This is called in a bottom-up traversal, so kids' overflows have
/// already been set.
/// So, currently this is a union of the overflows of all kids and our own
/// flow rectangle.
/// FIXME: Handle the overflow of absolute flow descendants, because their
/// assign-heights happen after the normal
/// assign-height-and-store-overflow traversal
fn store_overflow(self, _: &mut LayoutContext) { fn store_overflow(self, _: &mut LayoutContext) {
let my_position = mut_base(self).position; let my_position = mut_base(self).position;
let mut overflow = my_position; let mut overflow = my_position;
@ -723,17 +742,29 @@ impl<'a> MutableFlowUtils for &'a mut Flow {
/// For InlineFlow, add display items for all its boxes onto list`. /// For InlineFlow, add display items for all its boxes onto list`.
/// For BlockFlow, add a ClipDisplayItemClass for itself and its children, /// For BlockFlow, add a ClipDisplayItemClass for itself and its children,
/// plus any other display items like border. /// 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>( fn build_display_lists<E:ExtraDisplayListData>(
self, self,
builder: &DisplayListBuilder, builder: &DisplayListBuilder,
container_block_size: &Size2D<Au>, container_block_size: &Size2D<Au>,
absolute_cb_abs_position: Point2D<Au>,
dirty: &Rect<Au>, dirty: &Rect<Au>,
mut index: uint, mut index: uint,
lists: &RefCell<DisplayListCollection<E>>) lists: &RefCell<DisplayListCollection<E>>)
-> bool { -> bool {
debug!("Flow: building display list"); debug!("Flow: building display list");
index = match self.class() { 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), InlineFlowClass => self.as_inline().build_display_list_inline(builder, container_block_size, dirty, index, lists),
}; };
@ -745,21 +776,31 @@ impl<'a> MutableFlowUtils for &'a mut Flow {
let mut child_lists = DisplayListCollection::new(); let mut child_lists = DisplayListCollection::new();
child_lists.add_list(DisplayList::new()); child_lists.add_list(DisplayList::new());
let child_lists = RefCell::new(child_lists); let child_lists = RefCell::new(child_lists);
let container_block_size = match self.class() { let is_positioned = self.as_block().is_positioned();
BlockFlowClass => { let container_block_size;
if self.as_block().box_.is_some() { let abs_cb_position;
self.as_block().box_.get_ref().border_box.get().size let flow_pos = base(self).abs_position;
match self.as_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;
abs_cb_position = if is_positioned {
let padding_box_pos = flow_pos + box_.border_box.get().origin
+ Point2D(box_.border.get().left, box_.border.get().top);
padding_box_pos
} else { } else {
base(self).position.size absolute_cb_abs_position
} };
},
_ => {
base(self).position.size
} }
}; None => fail!("Flow: block container should have a box_")
}
for kid in child_iter(self) { for kid in child_iter(self) {
kid.build_display_lists(builder, &container_block_size, dirty, 0u, &child_lists); kid.build_display_lists(builder, &container_block_size,
abs_cb_position,
dirty, 0u, &child_lists);
} }
let mut child_lists = Some(child_lists.unwrap()); let mut child_lists = Some(child_lists.unwrap());
@ -828,4 +869,3 @@ impl MutableOwnedFlowUtils for ~Flow {
self_borrowed.destroy(); self_borrowed.destroy();
} }
} }

View file

@ -657,18 +657,13 @@ impl Flow for InlineFlow {
{ {
let this = &mut *self; let this = &mut *self;
for box_ in this.boxes.iter() { 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 assert!(self.base.children.len() == 0,
let flags_info = self.base.flags_info.clone(); "InlineFlow: should not have children flows in the current layout implementation.");
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)
}
// There are no child contexts, so stop here. // There are no child contexts, so stop here.
// TODO(Issue #225): once there are 'inline-block' elements, this won't be // 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); self.assign_height(ctx);
} }
/// Calculate and set the height of this Flow.
///
/// CSS Section 10.6.1
fn assign_height(&mut self, _: &mut LayoutContext) { fn assign_height(&mut self, _: &mut LayoutContext) {
debug!("assign_height_inline: assigning height for flow"); 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(", ") ~"InlineFlow: " + self.boxes.map(|s| s.debug_str()).connect(", ")
} }
} }

View file

@ -23,6 +23,7 @@ use layout::wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode};
use extra::url::Url; use extra::url::Url;
use extra::arc::{Arc, MutexArc}; use extra::arc::{Arc, MutexArc};
use geom::point::Point2D;
use geom::rect::Rect; use geom::rect::Rect;
use geom::size::Size2D; use geom::size::Size2D;
use gfx::display_list::{ClipDisplayItemClass, DisplayItem, DisplayItemIterator}; use gfx::display_list::{ClipDisplayItemClass, DisplayItem, DisplayItemIterator};
@ -276,7 +277,7 @@ impl LayoutTask {
chan: LayoutChan, chan: LayoutChan,
constellation_chan: ConstellationChan, constellation_chan: ConstellationChan,
script_chan: ScriptChan, script_chan: ScriptChan,
render_chan: RenderChan<OpaqueNode>, render_chan: RenderChan<OpaqueNode>,
image_cache_task: ImageCacheTask, image_cache_task: ImageCacheTask,
opts: &Opts, opts: &Opts,
profiler_chan: ProfilerChan) profiler_chan: ProfilerChan)
@ -402,7 +403,7 @@ impl LayoutTask {
/// crash. /// crash.
fn exit_now(&mut self) { fn exit_now(&mut self) {
let (response_port, response_chan) = Chan::new(); let (response_port, response_chan) = Chan::new();
match self.parallel_traversal { match self.parallel_traversal {
None => {} None => {}
Some(ref mut traversal) => traversal.shutdown(), Some(ref mut traversal) => traversal.shutdown(),
@ -614,6 +615,7 @@ impl LayoutTask {
if data.goal == ReflowForDisplay { if data.goal == ReflowForDisplay {
profile(time::LayoutDispListBuildCategory, self.profiler_chan.clone(), || { profile(time::LayoutDispListBuildCategory, self.profiler_chan.clone(), || {
let root_size = flow::base(layout_root).position.size; 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(); let mut display_list_collection = DisplayListCollection::new();
display_list_collection.add_list(DisplayList::<OpaqueNode>::new()); display_list_collection.add_list(DisplayList::<OpaqueNode>::new());
let display_list_collection = ~RefCell::new(display_list_collection); let display_list_collection = ~RefCell::new(display_list_collection);
@ -621,14 +623,16 @@ impl LayoutTask {
let display_list_builder = DisplayListBuilder { let display_list_builder = DisplayListBuilder {
ctx: &layout_ctx, 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 display_list_collection = Arc::new(display_list_collection.unwrap());
let mut color = color::rgba(255.0, 255.0, 255.0, 255.0); let mut color = color::rgba(255.0, 255.0, 255.0, 255.0);
for child in node.traverse_preorder() { for child in node.traverse_preorder() {
if child.type_id() == ElementNodeTypeId(HTMLHtmlElementTypeId) || if child.type_id() == ElementNodeTypeId(HTMLHtmlElementTypeId) ||
child.type_id() == ElementNodeTypeId(HTMLBodyElementTypeId) { child.type_id() == ElementNodeTypeId(HTMLBodyElementTypeId) {
let element_bg_color = { let element_bg_color = {
let thread_safe_child = ThreadSafeLayoutNode::new(&child); let thread_safe_child = ThreadSafeLayoutNode::new(&child);
@ -768,7 +772,7 @@ impl LayoutTask {
Au::from_frac_px(point.y as f64)); Au::from_frac_px(point.y as f64));
let resp = hit_test(x,y,display_list.list); let resp = hit_test(x,y,display_list.list);
if resp.is_some() { if resp.is_some() {
reply_chan.send(Ok(resp.unwrap())); reply_chan.send(Ok(resp.unwrap()));
return return
} }
} }
@ -842,4 +846,3 @@ impl LayoutTask {
util::replace(layout_data_ref.get(), None)); util::replace(layout_data_ref.get(), None));
} }
} }

View file

@ -119,6 +119,18 @@ impl FlowParallelInfo {
/// A parallel bottom-up flow traversal. /// A parallel bottom-up flow traversal.
trait ParallelPostorderFlowTraversal : PostorderFlowTraversal { 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, fn run_parallel(&mut self,
mut unsafe_flow: UnsafeFlow, mut unsafe_flow: UnsafeFlow,
_: &mut WorkerProxy<*mut LayoutContext,PaddedUnsafeFlow>) { _: &mut WorkerProxy<*mut LayoutContext,PaddedUnsafeFlow>) {
@ -144,8 +156,9 @@ trait ParallelPostorderFlowTraversal : PostorderFlowTraversal {
break break
} }
// No, we're not at the root yet. Then are we the last sibling of our parent? If // No, we're not at the root yet. Then are we the last child
// so, we can continue on with our parent; otherwise, we've gotta wait. // 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: &mut ~Flow = cast::transmute(&unsafe_parent);
let parent_base = flow::mut_base(*parent); let parent_base = flow::mut_base(*parent);
if parent_base.parallel.children_count.fetch_sub(1, SeqCst) == 1 { 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() queue.data = ptr::mut_null()
} }

View file

@ -0,0 +1,26 @@
<html>
<head>
<style>
#first {
position: relative;
width: 90px;
height: 90px;
border: solid 1px;
}
#abs {
position: absolute;
left: 30px;
top: 30px;
right: 30px;
bottom: 30px;
background: green;
}
</style>
</head>
<body>
<div id="first">
<div id="abs">
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,29 @@
<html>
<head>
<style>
#first {
width: 90px;
height: 90px;
border: solid 1px;
}
.row {
width: 90px;
height: 30px;
}
.center {
margin-left: 30px;
height: 30px;
width: 30px;
background: green;
}
</style>
</head>
<body>
<div id="first">
<div class="row"></div>
<div class="center">
</div>
<div class="row"></div>
</div>
</body>
</html>

View file

@ -0,0 +1,30 @@
<html>
<head>
<style>
body {
position: relative;
}
div {
width: 100px;
height: 100px;
}
#first {
background: red;
}
#abs {
position: absolute;
left: auto;
top: 0px;
background: green;
}
</style>
</head>
<body>
<div id="first">
</div>
<div id="abs">
</div>
</body>
</html>

View file

@ -0,0 +1,18 @@
<html>
<head>
<style>
div {
width: 100px;
height: 100px;
}
#first {
background: green;
}
</style>
</head>
<body>
<div id="first">
</div>
</body>
</html>

View file

@ -0,0 +1,26 @@
<html>
<head>
<style>
#first {
position: relative;
width: 100px;
height: 100px;
border: solid 1px;
}
#abs {
position: absolute;
left: 0px;
right: 50%;
top: 50%;
height: 50px;
background: green;
}
</style>
</head>
<body>
<div id="first">
<div id="abs">
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,27 @@
<html>
<head>
<style>
#first {
width: 100px;
height: 100px;
border: solid 1px;
}
.row {
width: 100px;
height: 50px;
}
.green_square {
height: 50px;
width: 50px;
background: green;
}
</style>
</head>
<body>
<div id="first">
<div class="row"></div>
<div class="green_square">
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,38 @@
<html>
<head>
<style>
html, body {
margin: 0px;
}
.box {
width: 100px;
height: 100px;
}
#rel-cont {
position: relative;
}
#first {
background: red;
}
#normal {
background: blue;
}
#fixed {
position: fixed;
left: 0px;
top: 0px;
background: green;
}
</style>
</head>
<body>
<div id="first" class="box">
</div>
<div id="normal" class="box"></div>
<!-- This should become empty -->
<div id="rel-cont">
<div id="fixed" class="box">
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,24 @@
<html>
<head>
<style>
html, body {
margin: 0px;
}
.box {
width: 100px;
height: 100px;
}
#first {
background: green;
}
#normal {
background: blue;
}
</style>
</head>
<body>
<div id="first" class="box">
</div>
<div id="normal" class="box"></div>
</body>
</html>