servo: Merge #412 - Remove the "tree" API and clean up flow a bit (from pcwalton:detree)

Source-Repo: https://github.com/servo/servo
Source-Revision: 0a6e2b8fd9d1acfe186148fca5fb0d289b616615
This commit is contained in:
Patrick Walton 2013-05-02 19:37:36 -07:00
Родитель 7ab65a8fa0
Коммит 9f0fa1bc6c
8 изменённых файлов: 240 добавлений и 432 удалений

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

@ -1,4 +1,7 @@
//use dom::bindings::clientrect::ClientRect;
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::bindings::utils::WrapperCache;
pub struct ClientRect {
@ -9,37 +12,41 @@ pub struct ClientRect {
right: f32,
}
pub impl ClientRect {
fn new(top: f32, bottom: f32, left: f32, right: f32) -> @mut ClientRect {
impl ClientRect {
pub fn new(top: f32, bottom: f32, left: f32, right: f32) -> @mut ClientRect {
let rect = @mut ClientRect {
top: top, bottom: bottom, left: left, right: right,
top: top,
bottom: bottom,
left: left,
right: right,
wrapper: WrapperCache::new()
};
rect.init_wrapper();
rect
}
fn Top(&self) -> f32 {
pub fn Top(&self) -> f32 {
self.top
}
fn Bottom(&self) -> f32 {
pub fn Bottom(&self) -> f32 {
self.bottom
}
fn Left(&self) -> f32 {
pub fn Left(&self) -> f32 {
self.left
}
fn Right(&self) -> f32 {
pub fn Right(&self) -> f32 {
self.right
}
fn Width(&self) -> f32 {
pub fn Width(&self) -> f32 {
f32::abs(self.right - self.left)
}
fn Height(&self) -> f32 {
pub fn Height(&self) -> f32 {
f32::abs(self.bottom - self.top)
}
}

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

@ -4,15 +4,14 @@
// Block layout.
use core::cell::Cell;
use layout::box::{RenderBox};
use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, FlowDisplayListBuilderMethods};
use layout::flow::{FlowContext, FlowTree, InlineBlockFlow, BlockFlow, RootFlow};
use layout::flow::{BlockFlow, FlowContext, InlineBlockFlow, RootFlow};
use layout::inline::InlineLayout;
use au = gfx::geometry;
use core::cell::Cell;
use geom::point::Point2D;
use geom::rect::Rect;
use gfx::display_list::DisplayList;
@ -82,7 +81,7 @@ impl BlockLayout for FlowContext {
let mut pref_width = Au(0);
/* find max width from child block contexts */
for FlowTree.each_child(self) |child_ctx| {
for self.each_child |child_ctx| {
assert!(child_ctx.starts_block_flow() || child_ctx.starts_inline_flow());
min_width = au::max(min_width, child_ctx.d().min_width);
@ -122,7 +121,7 @@ impl BlockLayout for FlowContext {
remaining_width -= left_used.add(&right_used);
}
for FlowTree.each_child(self) |child_ctx| {
for self.each_child |child_ctx| {
assert!(child_ctx.starts_block_flow() || child_ctx.starts_inline_flow());
child_ctx.d().position.origin.x = left_used;
child_ctx.d().position.size.width = remaining_width;
@ -134,7 +133,7 @@ impl BlockLayout for FlowContext {
let mut cur_y = Au(0);
for FlowTree.each_child(self) |child_ctx| {
for self.each_child |child_ctx| {
child_ctx.d().position.origin.y = cur_y;
cur_y += child_ctx.d().position.size.height;
}
@ -164,7 +163,7 @@ impl BlockLayout for FlowContext {
// TODO: handle any out-of-flow elements
// go deeper into the flow tree
for FlowTree.each_child(self) |child| {
for self.each_child |child| {
self.build_display_list_for_child(builder, child, dirty, offset, list)
}
}

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

@ -14,7 +14,6 @@ use layout::debug::{BoxedMutDebugMethods, DebugMethods};
use layout::flow::*;
use layout::inline::{InlineFlowData, InlineLayout};
use layout::root::RootFlowData;
use util::tree;
use gfx::image::holder::ImageHolder;
use servo_util::range::Range;
@ -223,9 +222,8 @@ impl BuilderContext {
priv fn attach_child_flow(&self, child: @mut FlowContext) {
let d = self.default_collector.flow.d(); // FIXME: borrow checker workaround
let cd = child.d(); // FIXME: borrow checker workaround
debug!("BuilderContext: Adding child flow f%? of f%?",
d.id, cd.id);
tree::add_child(&FlowTree, self.default_collector.flow, child);
debug!("BuilderContext: Adding child flow f%? of f%?", d.id, cd.id);
self.default_collector.flow.add_child(child);
}
priv fn create_child_flow_of_type(&self,
@ -332,10 +330,10 @@ pub impl LayoutTreeBuilder {
// eventually be elided or split, but the mapping between
// nodes and FlowContexts should not change during layout.
let flow = &mut this_ctx.default_collector.flow;
for tree::each_child(&FlowTree, flow) |child_flow: &@mut FlowContext| {
for flow.each_child |child_flow: @mut FlowContext| {
let node = child_flow.d().node;
assert!(node.has_layout_data());
node.layout_data().flow = Some(*child_flow);
node.layout_data().flow = Some(child_flow);
}
}
@ -355,8 +353,8 @@ pub impl LayoutTreeBuilder {
let mut found_child_block = false;
let flow = &mut parent_ctx.default_collector.flow;
for tree::each_child(&FlowTree, flow) |child_ctx: &@mut FlowContext| {
match **child_ctx {
for flow.each_child |child_ctx: @mut FlowContext| {
match *child_ctx {
InlineFlow(*) | InlineBlockFlow(*) => found_child_inline = true,
BlockFlow(*) => found_child_block = true,
_ => {}
@ -371,24 +369,36 @@ pub impl LayoutTreeBuilder {
// FIXME: this will create refcounted cycles between the removed flow and any
// of its RenderBox or FlowContext children, and possibly keep alive other junk
let parent_flow = parent_ctx.default_collector.flow;
// FIXME: Workaround for the borrow check.
let (first_child, last_child) = {
let parent_flow: &mut FlowContext = parent_flow;
let parent_flow_data = parent_flow.d();
(parent_flow_data.first_child, parent_flow_data.last_child)
};
// check first/last child for whitespace-ness
for tree::first_child(&FlowTree, &parent_flow).each |first_flow: &@mut FlowContext| {
for first_child.each |first_flow: &@mut FlowContext| {
if first_flow.starts_inline_flow() {
let boxes = &mut first_flow.inline().boxes;
if boxes.len() == 1 && boxes[0].is_whitespace_only() {
debug!("LayoutTreeBuilder: pruning whitespace-only first child flow f%d from parent f%d",
first_flow.d().id, parent_flow.d().id);
tree::remove_child(&FlowTree, parent_flow, *first_flow);
debug!("LayoutTreeBuilder: pruning whitespace-only first child flow \
f%d from parent f%d",
first_flow.d().id,
parent_flow.d().id);
parent_flow.remove_child(*first_flow);
}
}
}
for tree::last_child(&FlowTree, &parent_flow).each |last_flow: &@mut FlowContext| {
for last_child.each |last_flow: &@mut FlowContext| {
if last_flow.starts_inline_flow() {
let boxes = &mut last_flow.inline().boxes;
if boxes.len() == 1 && boxes.last().is_whitespace_only() {
debug!("LayoutTreeBuilder: pruning whitespace-only last child flow f%d from parent f%d",
last_flow.d().id, parent_flow.d().id);
tree::remove_child(&FlowTree, parent_flow, *last_flow);
debug!("LayoutTreeBuilder: pruning whitespace-only last child flow \
f%d from parent f%d",
last_flow.d().id,
parent_flow.d().id);
parent_flow.remove_child(*last_flow);
}
}
}
@ -416,7 +426,7 @@ pub impl LayoutTreeBuilder {
}
fn make_flow(&mut self, ty: FlowContextType, node: AbstractNode) -> @mut FlowContext {
let data = FlowData(self.next_flow_id(), node);
let data = FlowData::new(self.next_flow_id(), node);
let ret = match ty {
Flow_Absolute => @mut AbsoluteFlow(data),
Flow_Block => @mut BlockFlow(data, BlockFlowData()),

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

@ -2,8 +2,28 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use core;
use core::cell::Cell;
//! Servo's experimental layout system builds a tree of `FlowContext` and `RenderBox` objects and
/// solves layout constraints to obtain positions and display attributes of tree nodes. Positions
/// are computed in several tree traversals driven by the fundamental data dependencies required by
/// inline and block layout.
///
/// Flows are interior nodes in the layout tree and correspond closely to *flow contexts* in the
/// CSS specification. Flows are responsible for positioning their child flow contexts and render
/// boxes. Flows have purpose-specific fields, such as auxiliary line box structs, out-of-flow
/// child lists, and so on.
///
/// Currently, the important types of flows are:
///
/// * `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.
/// (In the future, this render box may be folded into `BlockFlow` to save space.)
///
/// * `InlineFlow`: a flow that establishes an inline context. It has a flat list of child
/// boxes/flows that are subject to inline layout and line breaking and structs to represent
/// line breaks and mapping to CSS boxes, for the purpose of handling `getClientRects()` and
/// similar methods.
use dom::node::AbstractNode;
use layout::block::{BlockFlowData, BlockLayout};
use layout::box::RenderBox;
@ -12,41 +32,16 @@ use layout::debug::BoxedMutDebugMethods;
use layout::display_list_builder::DisplayListBuilder;
use layout::inline::{InlineFlowData, InlineLayout};
use layout::root::{RootFlowData, RootLayout};
use util::tree;
use geom::rect::Rect;
use core::cell::Cell;
use core::ptr;
use geom::point::Point2D;
use geom::rect::Rect;
use gfx::display_list::DisplayList;
use gfx::geometry::Au;
/** Servo's experimental layout system builds a tree of FlowContexts
and RenderBoxes, and figures out positions and display attributes of
tree nodes. Positions are computed in several tree traversals driven
by fundamental data dependencies of inline and block layout.
Flows are interior nodes in the layout tree, and correspond closely to
flow contexts in the CSS specification. Flows are responsible for
positioning their child flow contexts and render boxes. Flows have
purpose-specific fields, such as auxilliary line box structs,
out-of-flow child lists, and so on.
Currently, the important types of flows are:
* BlockFlow: a flow that establishes a block context. It has several
child flows, each of which are positioned according to block
formatting context rules (as if child flows CSS block boxes). Block
flows also contain a single GenericBox to represent their rendered
borders, padding, etc. (In the future, this render box may be
folded into BlockFlow to save space.)
* InlineFlow: a flow that establishes an inline context. It has a
flat list of child boxes/flows that are subject to inline layout
and line breaking, and structs to represent line breaks and mapping
to CSS boxes, for the purpose of handling `getClientRects()`.
*/
/* The type of the formatting context, and data specific to each
context, such as linebox structures or float lists */
/// The type of the formatting context and data specific to each context, such as line box
/// structures or float lists.
pub enum FlowContext {
AbsoluteFlow(FlowData),
BlockFlow(FlowData, BlockFlowData),
@ -71,8 +66,13 @@ pub enum FlowContextType {
render boxes within the context. */
pub struct FlowData {
node: AbstractNode,
/* reference to parent, children flow contexts */
tree: tree::Tree<@mut FlowContext>,
parent: Option<@mut FlowContext>,
first_child: Option<@mut FlowContext>,
last_child: Option<@mut FlowContext>,
prev_sibling: Option<@mut FlowContext>,
next_sibling: Option<@mut FlowContext>,
/* TODO (Issue #87): debug only */
id: int,
@ -84,55 +84,131 @@ pub struct FlowData {
position: Rect<Au>,
}
pub fn FlowData(id: int, node: AbstractNode) -> FlowData {
FlowData {
node: node,
tree: tree::empty(),
id: id,
impl FlowData {
pub fn new(id: int, node: AbstractNode) -> FlowData {
FlowData {
node: node,
min_width: Au(0),
pref_width: Au(0),
position: Au::zero_rect()
parent: None,
first_child: None,
last_child: None,
prev_sibling: None,
next_sibling: None,
id: id,
min_width: Au(0),
pref_width: Au(0),
position: Au::zero_rect(),
}
}
}
pub impl<'self> FlowContext {
fn d(&'self mut self) -> &'self mut FlowData {
unsafe {
match *self {
AbsoluteFlow(ref d) => cast::transmute(d),
BlockFlow(ref d, _) => cast::transmute(d),
FloatFlow(ref d) => cast::transmute(d),
InlineBlockFlow(ref d) => cast::transmute(d),
InlineFlow(ref d, _) => cast::transmute(d),
RootFlow(ref d, _) => cast::transmute(d),
TableFlow(ref d) => cast::transmute(d)
impl<'self> FlowContext {
pub fn d(&'self mut self) -> &'self mut FlowData {
unsafe {
match *self {
AbsoluteFlow(ref d) => cast::transmute(d),
BlockFlow(ref d, _) => cast::transmute(d),
FloatFlow(ref d) => cast::transmute(d),
InlineBlockFlow(ref d) => cast::transmute(d),
InlineFlow(ref d, _) => cast::transmute(d),
RootFlow(ref d, _) => cast::transmute(d),
TableFlow(ref d) => cast::transmute(d)
}
}
}
}
fn inline(&'self mut self) -> &'self mut InlineFlowData {
/// Iterates over the immediate children of this flow.
///
/// TODO: Fold me into `util::tree`.
pub fn each_child(@mut self, f: &fn(@mut FlowContext) -> bool) {
let mut current_opt = self.d().first_child;
while !current_opt.is_none() {
let current = current_opt.get();
if !f(current) {
break;
}
current_opt = current.d().next_sibling;
}
}
/// Adds the given flow to the end of the list of this flow's children. The new child must be
/// detached from the tree before calling this method.
///
/// TODO: Fold me into `util::tree`.
pub fn add_child(@mut self, child: @mut FlowContext) {
let self_data = self.d(), child_data = child.d();
assert!(child_data.parent.is_none());
assert!(child_data.prev_sibling.is_none());
assert!(child_data.next_sibling.is_none());
match self_data.last_child {
None => {
self_data.first_child = Some(child);
}
Some(last_child) => {
assert!(last_child.d().next_sibling.is_none());
last_child.d().next_sibling = Some(child);
child_data.prev_sibling = Some(last_child);
}
}
self_data.last_child = Some(child);
child_data.parent = Some(self);
}
/// Removes the given flow from the tree.
///
/// TODO: Fold me into `util::tree`.
pub fn remove_child(@mut self, child: @mut FlowContext) {
let self_data = self.d(), child_data = child.d();
assert!(child_data.parent.is_some());
assert!(ptr::ref_eq(&*child_data.parent.get(), self));
match child_data.prev_sibling {
None => self_data.first_child = child_data.next_sibling,
Some(prev_sibling) => {
prev_sibling.d().next_sibling = child_data.next_sibling;
child_data.prev_sibling = None;
}
}
match child_data.next_sibling {
None => self_data.last_child = child.d().prev_sibling,
Some(next_sibling) => {
next_sibling.d().prev_sibling = Some(next_sibling);
child_data.next_sibling = None;
}
}
child_data.parent = None;
}
pub fn inline(&'self mut self) -> &'self mut InlineFlowData {
match self {
&InlineFlow(_, ref i) => unsafe { cast::transmute(i) },
_ => fail!(fmt!("Tried to access inline data of non-inline: f%d", self.d().id))
}
}
fn block(&'self mut self) -> &'self mut BlockFlowData {
pub fn block(&'self mut self) -> &'self mut BlockFlowData {
match self {
&BlockFlow(_, ref mut b) => unsafe { cast::transmute(b) },
_ => fail!(fmt!("Tried to access block data of non-block: f%d", self.d().id))
}
}
fn root(&'self mut self) -> &'self mut RootFlowData {
pub fn root(&'self mut self) -> &'self mut RootFlowData {
match self {
&RootFlow(_, ref r) => unsafe { cast::transmute(r) },
_ => fail!(fmt!("Tried to access root data of non-root: f%d", self.d().id))
}
}
fn bubble_widths(@mut self, ctx: &mut LayoutContext) {
pub fn bubble_widths(@mut self, ctx: &mut LayoutContext) {
match self {
@BlockFlow(*) => self.bubble_widths_block(ctx),
@InlineFlow(*) => self.bubble_widths_inline(ctx),
@ -141,7 +217,7 @@ pub impl<'self> FlowContext {
}
}
fn assign_widths(@mut self, ctx: &mut LayoutContext) {
pub fn assign_widths(@mut self, ctx: &mut LayoutContext) {
match self {
@BlockFlow(*) => self.assign_widths_block(ctx),
@InlineFlow(*) => self.assign_widths_inline(ctx),
@ -150,7 +226,7 @@ pub impl<'self> FlowContext {
}
}
fn assign_height(@mut self, ctx: &mut LayoutContext) {
pub fn assign_height(@mut self, ctx: &mut LayoutContext) {
match self {
@BlockFlow(*) => self.assign_height_block(ctx),
@InlineFlow(*) => self.assign_height_inline(ctx),
@ -159,8 +235,11 @@ pub impl<'self> FlowContext {
}
}
fn build_display_list_recurse(@mut self, builder: &DisplayListBuilder, dirty: &Rect<Au>,
offset: &Point2D<Au>, list: &Cell<DisplayList>) {
pub fn build_display_list_recurse(@mut self,
builder: &DisplayListBuilder,
dirty: &Rect<Au>,
offset: &Point2D<Au>,
list: &Cell<DisplayList>) {
let d = self.d(); // FIXME: borrow checker workaround
debug!("FlowContext::build_display_list at %?: %s", d.position, self.debug_str());
@ -173,9 +252,10 @@ pub impl<'self> FlowContext {
}
// Actual methods that do not require much flow-specific logic
fn foldl_all_boxes<B: Copy>(&mut self,
seed: B,
cb: &fn(a: B, b: @mut RenderBox) -> B) -> B {
pub fn foldl_all_boxes<B:Copy>(&mut self,
seed: B,
cb: &fn(a: B, b: @mut RenderBox) -> B)
-> B {
match self {
&RootFlow(*) => {
let root = self.root(); // FIXME: borrow checker workaround
@ -193,85 +273,67 @@ pub impl<'self> FlowContext {
}
}
fn foldl_boxes_for_node<B: Copy>(&mut self,
node: AbstractNode,
seed: B,
cb: &fn(a: B,@mut RenderBox) -> B)
-> B {
pub fn foldl_boxes_for_node<B:Copy>(&mut self,
node: AbstractNode,
seed: B,
cb: &fn(a: B, @mut RenderBox) -> B)
-> B {
do self.foldl_all_boxes(seed) |acc, box| {
if box.d().node == node { cb(acc, box) }
else { acc }
}
}
fn iter_all_boxes<T>(&mut self, cb: &fn(@mut RenderBox) -> T) {
pub fn iter_all_boxes(&mut self, cb: &fn(@mut RenderBox) -> bool) {
match self {
&RootFlow(*) => {
let root = self.root(); // FIXME: borrow checker workaround
for root.box.each |box| { cb(*box); }
for root.box.each |box| {
if !cb(*box) {
break;
}
}
}
&BlockFlow(*) => {
let block = self.block(); // FIXME: borrow checker workaround
for block.box.each |box| { cb(*box); }
for block.box.each |box| {
if !cb(*box) {
break;
}
}
}
&InlineFlow(*) => {
let inline = self.inline(); // FIXME: borrow checker workaround
for inline.boxes.each |box| { cb(*box); }
for inline.boxes.each |box| {
if !cb(*box) {
break;
}
}
}
_ => fail!(fmt!("Don't know how to iterate node's RenderBoxes for %?", self))
}
}
fn iter_boxes_for_node<T>(&mut self,
node: AbstractNode,
cb: &fn(@mut RenderBox) -> T) {
do self.iter_all_boxes |box| {
if box.d().node == node { cb(box); }
pub fn iter_boxes_for_node(&mut self, node: AbstractNode, cb: &fn(@mut RenderBox) -> bool) {
for self.iter_all_boxes |box| {
if box.d().node == node {
if !cb(box) {
break;
}
}
}
}
}
/* The tree holding FlowContexts */
pub enum FlowTree { FlowTree }
impl FlowTree {
fn each_child(&self, ctx: @mut FlowContext, f: &fn(box: @mut FlowContext) -> bool) {
tree::each_child(self, &ctx, |box| f(*box) )
}
}
impl tree::ReadMethods<@mut FlowContext> for FlowTree {
fn with_tree_fields<R>(&self, box: &@mut FlowContext, f: &fn(&mut tree::Tree<@mut FlowContext>) -> R) -> R {
let tree = &mut box.d().tree;
f(tree)
}
}
impl FlowTree {
fn add_child(self, parent: @mut FlowContext, child: @mut FlowContext) {
tree::add_child(&self, parent, child)
}
}
impl tree::WriteMethods<@mut FlowContext> for FlowTree {
fn tree_eq(&self, a: &@mut FlowContext, b: &@mut FlowContext) -> bool { core::managed::mut_ptr_eq(*a, *b) }
fn with_tree_fields<R>(&self, box: &@mut FlowContext, f: &fn(&mut tree::Tree<@mut FlowContext>) -> R) -> R {
let tree = &mut box.d().tree;
f(tree)
}
}
impl BoxedMutDebugMethods for FlowContext {
fn dump(@mut self) {
self.dump_indent(0u);
self.dump_indent(0);
}
/** Dumps the flow tree, for debugging, with indentation. */
/// Dumps the flow tree, for debugging, with indentation.
fn dump_indent(@mut self, indent: uint) {
let mut s = ~"|";
for uint::range(0u, indent) |_i| {
for uint::range(0, indent) |_i| {
s += ~"---- ";
}
@ -279,8 +341,8 @@ impl BoxedMutDebugMethods for FlowContext {
debug!("%s", s);
// FIXME: this should have a pure/const version?
for FlowTree.each_child(self) |child| {
child.dump_indent(indent + 1u)
for self.each_child |child| {
child.dump_indent(indent + 1)
}
}
@ -313,3 +375,4 @@ impl BoxedMutDebugMethods for FlowContext {
fmt!("f%? %?", d.id, repr)
}
}

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

@ -10,7 +10,7 @@ use gfx::geometry::Au;
use layout::block::BlockLayout;
use layout::box::RenderBox;
use layout::context::LayoutContext;
use layout::flow::{FlowContext, FlowTree, RootFlow};
use layout::flow::{FlowContext, RootFlow};
use layout::display_list_builder::DisplayListBuilder;
pub struct RootFlowData {
@ -63,7 +63,7 @@ impl RootLayout for FlowContext {
// the root adjusts self height to at least cover the viewport.
let mut cur_y = Au(0);
for FlowTree.each_child(self) |child_ctx| {
for self.each_child |child_ctx| {
child_ctx.d().position.origin.y = cur_y;
cur_y += child_ctx.d().position.size.height;
}

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

@ -2,9 +2,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use layout::flow::{FlowContext, FlowTree};
use layout::flow::FlowContext;
/** Trait for running tree-based traversals over layout contexts */
/// A trait for running tree-based traversals over flows contexts.
pub trait FlowContextTraversals {
fn traverse_preorder(@mut self, preorder_cb: &fn(@mut FlowContext));
fn traverse_postorder(@mut self, postorder_cb: &fn(@mut FlowContext));
@ -13,11 +13,15 @@ pub trait FlowContextTraversals {
impl FlowContextTraversals for FlowContext {
fn traverse_preorder(@mut self, preorder_cb: &fn(@mut FlowContext)) {
preorder_cb(self);
do FlowTree.each_child(self) |child| { child.traverse_preorder(preorder_cb); true }
for self.each_child |child| {
child.traverse_preorder(preorder_cb);
}
}
fn traverse_postorder(@mut self, postorder_cb: &fn(@mut FlowContext)) {
do FlowTree.each_child(self) |child| { child.traverse_postorder(postorder_cb); true }
for self.each_child |child| {
child.traverse_postorder(postorder_cb);
}
postorder_cb(self);
}
}

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

@ -5,6 +5,5 @@
pub use servo_util::cache;
pub use servo_util::time;
pub mod tree;
pub mod task;

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

@ -1,274 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// A generic tree datatype.
//
// TODO: Use traits.
pub struct Tree<T> {
parent: Option<T>,
first_child: Option<T>,
last_child: Option<T>,
prev_sibling: Option<T>,
next_sibling: Option<T>
}
pub trait ReadMethods<T> {
fn with_tree_fields<R>(&self, &T, f: &fn(&mut Tree<T>) -> R) -> R;
}
pub trait WriteMethods<T> {
fn with_tree_fields<R>(&self, &T, f: &fn(&mut Tree<T>) -> R) -> R;
fn tree_eq(&self, &T, &T) -> bool;
}
pub fn each_child<T:Copy,O:ReadMethods<T>>(ops: &O, node: &T, f: &fn(&T) -> bool) {
let mut p = ops.with_tree_fields(node, |f| f.first_child);
loop {
match copy p {
None => { return; }
Some(ref c) => {
if !f(c) { return; }
p = ops.with_tree_fields(c, |f| f.next_sibling);
}
}
}
}
pub fn is_leaf<T:Copy,O:ReadMethods<T>>(ops: &O, node: &T) -> bool {
first_child(ops, node).is_none()
}
pub fn first_child<T:Copy,O:ReadMethods<T>>(ops: &O, node: &T) -> Option<T> {
ops.with_tree_fields(node, |tf| tf.first_child)
}
pub fn last_child<T:Copy,O:ReadMethods<T>>(ops: &O, node: &T) -> Option<T> {
ops.with_tree_fields(node, |tf| tf.last_child)
}
pub fn next_sibling<T:Copy,O:ReadMethods<T>>(ops: &O, node: &T) -> Option<T> {
ops.with_tree_fields(node, |tf| tf.next_sibling)
}
pub fn prev_sibling<T:Copy,O:ReadMethods<T>>(ops: &O, node: &T) -> Option<T> {
ops.with_tree_fields(node, |tf| tf.prev_sibling)
}
pub fn parent<T:Copy,O:ReadMethods<T>>(ops: &O, node: &T) -> Option<T> {
ops.with_tree_fields(node, |tf| tf.parent)
}
pub fn empty<T>() -> Tree<T> {
Tree {
mut parent: None,
mut first_child: None,
mut last_child: None,
mut prev_sibling: None,
mut next_sibling: None
}
}
pub fn add_child<T:Copy,O:WriteMethods<T>>(ops: &O, parent: T, child: T) {
assert!(!ops.tree_eq(&parent, &child));
ops.with_tree_fields(&child, |child_tf| {
match child_tf.parent {
Some(_) => { fail!(~"Already has a parent"); }
None => { child_tf.parent = Some(parent); }
}
assert!(child_tf.prev_sibling.is_none());
assert!(child_tf.next_sibling.is_none());
ops.with_tree_fields(&parent, |parent_tf| {
match copy parent_tf.last_child {
None => {
parent_tf.first_child = Some(child);
}
Some(lc) => {
ops.with_tree_fields(&lc, |lc_tf| {
assert!(lc_tf.next_sibling.is_none());
lc_tf.next_sibling = Some(child);
});
child_tf.prev_sibling = Some(lc);
}
}
parent_tf.last_child = Some(child);
});
});
}
pub fn remove_child<T:Copy,O:WriteMethods<T>>(ops: &O, parent: T, child: T) {
do ops.with_tree_fields(&child) |child_tf| {
match copy child_tf.parent {
None => { fail!(~"Not a child"); }
Some(parent_n) => {
assert!(ops.tree_eq(&parent, &parent_n));
// adjust parent fields
do ops.with_tree_fields(&parent) |parent_tf| {
match copy parent_tf.first_child {
None => { fail!(~"parent had no first child??") },
Some(first_child) if ops.tree_eq(&child, &first_child) => {
parent_tf.first_child = child_tf.next_sibling;
},
Some(_) => {}
};
match copy parent_tf.last_child {
None => { fail!(~"parent had no last child??") },
Some(last_child) if ops.tree_eq(&child, &last_child) => {
parent_tf.last_child = child_tf.prev_sibling;
},
Some(_) => {}
}
}
}
}
// adjust siblings
match child_tf.prev_sibling {
None => {},
Some(_) => {
do ops.with_tree_fields(&child_tf.prev_sibling.get()) |prev_tf| {
prev_tf.next_sibling = child_tf.next_sibling;
}
}
}
match child_tf.next_sibling {
None => {},
Some(_) => {
do ops.with_tree_fields(&child_tf.next_sibling.get()) |next_tf| {
next_tf.prev_sibling = child_tf.prev_sibling;
}
}
}
// clear child
child_tf.parent = None;
child_tf.next_sibling = None;
child_tf.prev_sibling = None;
}
}
pub fn get_parent<T:Copy,O:ReadMethods<T>>(ops: &O, node: &T) -> Option<T> {
ops.with_tree_fields(node, |tf| tf.parent)
}
#[cfg(test)]
mod test {
use super::*;
use core::managed::mut_ptr_eq;
struct dummy {
fields: Tree<@mut dummy>,
value: uint
}
enum dtree { dtree }
impl ReadMethods<@mut dummy> for dtree {
fn with_tree_fields<R>(&self, d: &@mut dummy, f: &fn(&mut Tree<@mut dummy>) -> R) -> R {
f(&mut d.fields)
}
}
impl WriteMethods<@mut dummy> for dtree {
fn with_tree_fields<R>(&self, d: &@mut dummy, f: &fn(&mut Tree<@mut dummy>) -> R) -> R {
f(&mut d.fields)
}
fn tree_eq(&self, a: &@mut dummy, b: &@mut dummy) -> bool { mut_ptr_eq(*a, *b) }
}
fn new_dummy(v: uint) -> @mut dummy {
@mut dummy {fields: empty(), value: v}
}
fn parent_with_3_children() -> (@mut dummy, ~[@mut dummy]) {
let children = ~[new_dummy(0u),
new_dummy(1u),
new_dummy(2u)];
let p = new_dummy(3u);
for vec::each(children) |c| {
add_child(&dtree, p, *c);
}
return (p, children);
}
#[test]
fn add_child_0() {
let (p, children) = parent_with_3_children();
let mut i = 0u;
for each_child(&dtree, &p) |c| {
assert!(c.value == i);
i += 1u;
}
assert!(i == children.len());
}
#[test]
fn add_child_break() {
let (p, _) = parent_with_3_children();
let mut i = 0u;
for each_child(&dtree, &p) |_c| {
i += 1u;
break;
}
assert!(i == 1u);
}
#[test]
fn remove_first_child() {
let (p, children) = parent_with_3_children();
remove_child(&dtree, p, children[0]);
let mut i = 0;
for each_child(&dtree, &p) |_c| {
i += 1;
}
assert!(i == 2);
}
#[test]
fn remove_last_child() {
let (p, children) = parent_with_3_children();
remove_child(&dtree, p, children[2]);
let mut i = 0;
for each_child(&dtree, &p) |_c| {
i += 1;
}
assert!(i == 2);
}
#[test]
fn remove_middle_child() {
let (p, children) = parent_with_3_children();
remove_child(&dtree, p, children[1]);
let mut i = 0;
for each_child(&dtree, &p) |_c| {
i += 1;
}
assert!(i == 2);
}
#[test]
fn remove_all_child() {
let (p, children) = parent_with_3_children();
remove_child(&dtree, p, children[0]);
remove_child(&dtree, p, children[1]);
remove_child(&dtree, p, children[2]);
let mut i = 0;
for each_child(&dtree, &p) |_c| {
i += 1;
}
assert!(i == 0);
}
}