зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
7ab65a8fa0
Коммит
9f0fa1bc6c
|
@ -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);
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче