layout: Port the flow tree over to the new generic tree functions

This commit is contained in:
Patrick Walton 2013-05-06 20:05:17 -07:00
parent 6a6cad1e39
commit fe36f1dccb
7 changed files with 116 additions and 129 deletions

View file

@ -16,6 +16,7 @@ use geom::point::Point2D;
use geom::rect::Rect;
use gfx::display_list::DisplayList;
use gfx::geometry::Au;
use servo_util::tree::TreeUtils;
pub struct BlockFlowData {
/// Data common to all flows.

View file

@ -19,6 +19,7 @@ use gfx::image::holder::ImageHolder;
use newcss::values::{CSSDisplay, CSSDisplayBlock, CSSDisplayInline, CSSDisplayInlineBlock};
use newcss::values::{CSSDisplayNone};
use servo_util::range::Range;
use servo_util::tree::TreeUtils;
pub struct LayoutTreeBuilder {
root_flow: Option<FlowContext>,
@ -121,7 +122,6 @@ impl BoxGenerator {
// depending on flow, make a box for this node.
match self.flow {
InlineFlow(inline) => {
use servo_util::tree::TreeUtils; // For `is_leaf()`.
let mut inline = &mut *inline;
let node_range_start = inline.boxes.len();
@ -338,12 +338,8 @@ pub impl LayoutTreeBuilder {
debug!("point b: %s", cur_node.debug_str());
// recurse on child nodes.
{
use servo_util::tree::TreeUtils; // For `each_child()`.
for cur_node.each_child |child_node| {
self.construct_recursively(layout_ctx, child_node, &mut this_ctx);
}
for cur_node.each_child |child_node| {
self.construct_recursively(layout_ctx, child_node, &mut this_ctx);
}
this_ctx.default_collector.pop_node(layout_ctx, self, cur_node);
@ -354,6 +350,7 @@ 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;
let flow: &FlowContext = flow;
for flow.each_child |child_flow| {
do child_flow.with_common_info |child_flow_info| {
let node = child_flow_info.node;
@ -379,6 +376,7 @@ pub impl LayoutTreeBuilder {
let mut found_child_block = false;
let flow = &mut parent_ctx.default_collector.flow;
let flow: &FlowContext = flow;
for flow.each_child |child_ctx| {
match child_ctx {
InlineFlow(*) | InlineBlockFlow(*) => found_child_inline = true,

View file

@ -41,6 +41,7 @@ use geom::point::Point2D;
use geom::rect::Rect;
use gfx::display_list::DisplayList;
use gfx::geometry::Au;
use servo_util::tree::{TreeNode, TreeNodeRef, TreeUtils};
/// The type of the formatting context and data specific to each context, such as line box
/// structures or float lists.
@ -64,6 +65,21 @@ pub enum FlowContextType {
Flow_Table
}
impl Clone for FlowContext {
fn clone(&self) -> FlowContext {
*self
}
}
impl TreeNodeRef<FlowData> for FlowContext {
fn with_immutable_node<R>(&self, callback: &fn(&FlowData) -> R) -> R {
self.with_common_imm_info(callback)
}
fn with_mutable_node<R>(&self, callback: &fn(&mut FlowData) -> R) -> R {
self.with_common_info(callback)
}
}
/// Data common to all flows.
///
/// FIXME: We need a naming convention for pseudo-inheritance like this. How about
@ -88,6 +104,48 @@ pub struct FlowData {
position: Rect<Au>,
}
impl TreeNode<FlowContext> for FlowData {
fn parent_node(&self) -> Option<FlowContext> {
self.parent
}
fn first_child(&self) -> Option<FlowContext> {
self.first_child
}
fn last_child(&self) -> Option<FlowContext> {
self.last_child
}
fn prev_sibling(&self) -> Option<FlowContext> {
self.prev_sibling
}
fn next_sibling(&self) -> Option<FlowContext> {
self.next_sibling
}
fn set_parent_node(&mut self, new_parent_node: Option<FlowContext>) {
self.parent = new_parent_node
}
fn set_first_child(&mut self, new_first_child: Option<FlowContext>) {
self.first_child = new_first_child
}
fn set_last_child(&mut self, new_last_child: Option<FlowContext>) {
self.last_child = new_last_child
}
fn set_prev_sibling(&mut self, new_prev_sibling: Option<FlowContext>) {
self.prev_sibling = new_prev_sibling
}
fn set_next_sibling(&mut self, new_next_sibling: Option<FlowContext>) {
self.next_sibling = new_next_sibling
}
}
impl FlowData {
pub fn new(id: int, node: AbstractNode) -> FlowData {
FlowData {
@ -109,6 +167,30 @@ impl FlowData {
}
impl<'self> FlowContext {
// FIXME: This method is a duplicate of `with_immutable_node`; fix this.
#[inline(always)]
pub fn with_common_imm_info<R>(&self, block: &fn(&FlowData) -> R) -> R {
match *self {
AbsoluteFlow(info) => block(info),
BlockFlow(info) => {
let info = &*info; // FIXME: Borrow check workaround.
block(&info.common)
}
FloatFlow(info) => block(info),
InlineBlockFlow(info) => block(info),
InlineFlow(info) => {
let info = &*info; // FIXME: Borrow check workaround.
block(&info.common)
}
RootFlow(info) => {
let info = &*info; // FIXME: Borrow check workaround.
block(&info.common)
}
TableFlow(info) => block(info),
}
}
// FIXME: This method is a duplicate of `with_mutable_node`; fix this.
#[inline(always)]
pub fn with_common_info<R>(&self, block: &fn(&mut FlowData) -> R) -> R {
match *self {
@ -145,87 +227,6 @@ impl<'self> FlowContext {
}
}
/// Iterates over the immediate children of this flow.
///
/// TODO: Fold me into `util::tree`.
pub fn each_child(&self, f: &fn(FlowContext) -> bool) {
let mut current_opt = self.with_common_info(|info| info.first_child);
while !current_opt.is_none() {
let current = current_opt.get();
if !f(current) {
break;
}
current_opt = current.with_common_info(|info| info.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(&self, child: FlowContext) {
do self.with_common_info |self_info| {
do child.with_common_info |child_info| {
assert!(child_info.parent.is_none());
assert!(child_info.prev_sibling.is_none());
assert!(child_info.next_sibling.is_none());
match self_info.last_child {
None => {
self_info.first_child = Some(child);
}
Some(last_child) => {
do last_child.with_common_info |last_child_info| {
assert!(last_child_info.next_sibling.is_none());
last_child_info.next_sibling = Some(child);
child_info.prev_sibling = Some(last_child);
}
}
}
self_info.last_child = Some(child);
child_info.parent = Some(*self);
}
}
}
/// Removes the given flow from the tree.
///
/// TODO: Fold me into `util::tree`.
pub fn remove_child(&self, child: FlowContext) {
do self.with_common_info |self_info| {
do child.with_common_info |child_info| {
assert!(child_info.parent.is_some());
match child_info.prev_sibling {
None => self_info.first_child = child_info.next_sibling,
Some(prev_sibling) => {
do prev_sibling.with_common_info |prev_sibling_info| {
prev_sibling_info.next_sibling = child_info.next_sibling;
child_info.prev_sibling = None;
}
}
}
match child_info.next_sibling {
None => {
do child.with_common_info |child_info| {
self_info.last_child = child_info.prev_sibling;
}
}
Some(next_sibling) => {
do next_sibling.with_common_info |next_sibling_info| {
next_sibling_info.prev_sibling = Some(next_sibling);
child_info.next_sibling = None;
}
}
}
child_info.parent = None;
}
}
}
pub fn inline(&self) -> @mut InlineFlowData {
match *self {
InlineFlow(info) => info,

View file

@ -15,7 +15,6 @@ use layout::context::LayoutContext;
use layout::debug::{BoxedMutDebugMethods, DebugMethods};
use layout::display_list_builder::{DisplayListBuilder, FlowDisplayListBuilderMethods};
use layout::flow::FlowContext;
use layout::traverse::*;
use resource::image_cache_task::{ImageCacheTask, ImageResponseMsg};
use resource::local_image_cache::LocalImageCache;
use util::task::spawn_listener;
@ -35,6 +34,7 @@ use gfx::render_task::{RenderMsg, RenderTask};
use newcss::select::SelectCtx;
use newcss::stylesheet::Stylesheet;
use newcss::types::OriginAuthor;
use servo_util::tree::TreeUtils;
use std::net::url::Url;
pub type LayoutTask = SharedChan<Msg>;
@ -165,11 +165,12 @@ impl Layout {
self.css_select_ctx.append_sheet(sheet.take(), OriginAuthor);
}
/// The high-level routine that performs layout tasks.
fn handle_build(&mut self, data: &BuildData) {
let node = &data.node;
// FIXME: Bad copy
// FIXME: Bad copy!
let doc_url = copy data.url;
// FIXME: Bad clone
// FIXME: Bad clone!
let dom_event_chan = data.dom_event_chan.clone();
debug!("layout: received layout request for: %s", doc_url.to_str());
@ -177,12 +178,13 @@ impl Layout {
debug!("layout: parsed Node tree");
debug!("%?", node.dump());
// Reset the image cache
// Reset the image cache.
self.local_image_cache.next_round(self.make_on_image_available_cb(dom_event_chan));
let screen_size = Size2D(Au::from_px(data.window_size.width as int),
Au::from_px(data.window_size.height as int));
// Create a layout context for use throughout the following passes.
let mut layout_ctx = LayoutContext {
image_cache: self.local_image_cache,
font_ctx: self.font_ctx,
@ -190,8 +192,10 @@ impl Layout {
screen_size: Rect(Point2D(Au(0), Au(0)), screen_size)
};
// Initialize layout data for each node.
//
// FIXME: This is inefficient. We don't need an entire traversal to do this!
do time("layout: aux initialization") {
// TODO: this is dumb. we don't need an entire traversal to do this
node.initialize_style_for_subtree(&mut self.layout_refs);
}
@ -205,6 +209,7 @@ impl Layout {
}
}
// Construct the flow tree.
let layout_root: FlowContext = do time("layout: tree construction") {
let mut builder = LayoutTreeBuilder::new();
let layout_root: FlowContext = match builder.construct_trees(&layout_ctx, *node) {
@ -218,13 +223,21 @@ impl Layout {
layout_root
};
// Perform the primary layout passes over the flow tree to compute the locations of all
// the boxes.
do time("layout: main layout") {
/* perform layout passes over the flow tree */
do layout_root.traverse_postorder |f| { f.bubble_widths(&mut layout_ctx) }
do layout_root.traverse_preorder |f| { f.assign_widths(&mut layout_ctx) }
do layout_root.traverse_postorder |f| { f.assign_height(&mut layout_ctx) }
for layout_root.traverse_postorder |flow| {
flow.bubble_widths(&mut layout_ctx);
};
for layout_root.traverse_preorder |flow| {
flow.assign_widths(&mut layout_ctx);
};
for layout_root.traverse_postorder |flow| {
flow.assign_height(&mut layout_ctx);
};
}
// Build the display list, and send it to the renderer.
do time("layout: display list building") {
let builder = DisplayListBuilder {
ctx: &layout_ctx,
@ -249,13 +262,13 @@ impl Layout {
self.render_task.send(RenderMsg(render_layer));
} // time(layout: display list building)
// Tell content we're done
// Tell content that we're done.
data.content_join_chan.send(());
}
fn handle_query(&self, query: LayoutQuery,
reply_chan: Chan<LayoutQueryResponse>) {
/// Handles a query from the script task. This is the main routine that DOM functions like
/// `getClientRects()` or `getBoundingClientRect()` ultimately invoke.
fn handle_query(&self, query: LayoutQuery, reply_chan: Chan<LayoutQueryResponse>) {
match query {
ContentBox(node) => {
let response = match node.layout_data().flow {

View file

@ -13,6 +13,8 @@ use layout::context::LayoutContext;
use layout::flow::{FlowContext, FlowData, RootFlow};
use layout::display_list_builder::DisplayListBuilder;
use servo_util::tree::TreeUtils;
pub struct RootFlowData {
/// Data common to all flows.
common: FlowData,

View file

@ -1,27 +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/. */
use layout::flow::FlowContext;
/// A trait for running tree-based traversals over flows contexts.
pub trait FlowContextTraversals {
fn traverse_preorder(&self, preorder_cb: &fn(FlowContext));
fn traverse_postorder(&self, postorder_cb: &fn(FlowContext));
}
impl FlowContextTraversals for FlowContext {
fn traverse_preorder(&self, preorder_cb: &fn(FlowContext)) {
preorder_cb(*self);
for self.each_child |child| {
child.traverse_preorder(preorder_cb);
}
}
fn traverse_postorder(&self, postorder_cb: &fn(FlowContext)) {
for self.each_child |child| {
child.traverse_postorder(postorder_cb);
}
postorder_cb(*self);
}
}

View file

@ -104,7 +104,6 @@ pub mod layout {
pub mod inline;
pub mod root;
pub mod text;
pub mod traverse;
mod aux;
}