mirror of
https://github.com/servo/servo.git
synced 2025-08-07 06:25:32 +01:00
Added the ability to iterate over the layout-box tree in parallel.
This commit is contained in:
parent
f2c97474c5
commit
fa122b2a78
7 changed files with 237 additions and 41 deletions
|
@ -4,23 +4,23 @@ import dom::base::{Element, ElementKind, HTMLDivElement, HTMLImageElement, Node,
|
|||
import dom::base::{NodeKind};
|
||||
import dom::rcu;
|
||||
import dom::rcu::ReaderMethods;
|
||||
import dom::style::Unit;
|
||||
import gfx::geometry;
|
||||
import gfx::geometry::{au, zero_size_au};
|
||||
import geom::point::Point2D;
|
||||
import geom::rect::Rect;
|
||||
import geom::size::Size2D;
|
||||
import image::base::image;
|
||||
import image::base::{image, load};
|
||||
import layout::block::block_layout_methods;
|
||||
import layout::inline::inline_layout_methods;
|
||||
import util::tree;
|
||||
import util::color::{Color, css_colors};
|
||||
import text::text_box;
|
||||
import style::style::SpecifiedStyle;
|
||||
import style::style::{SpecifiedStyle, default_style_methods};
|
||||
import text::text_layout_methods;
|
||||
import vec::{push, push_all};
|
||||
|
||||
import future::future;
|
||||
import arc::arc;
|
||||
import arc::{arc, clone};
|
||||
|
||||
enum BoxKind {
|
||||
BlockBox,
|
||||
|
@ -30,12 +30,35 @@ enum BoxKind {
|
|||
}
|
||||
|
||||
class Appearance {
|
||||
let mut background_image: option<~future<~arc<~image>>>;
|
||||
let mut background_image: option<ImageHolder>;
|
||||
let mut background_color: Color;
|
||||
let mut width: Unit;
|
||||
let mut height: Unit;
|
||||
|
||||
new() {
|
||||
new(kind: NodeKind) {
|
||||
self.background_image = none;
|
||||
self.background_color = css_colors::black();
|
||||
self.background_color = kind.default_color();
|
||||
self.width = kind.default_width();
|
||||
self.height = kind.default_height();
|
||||
}
|
||||
|
||||
// This will be very unhappy if it is getting run in parallel with
|
||||
// anything trying to read the background image
|
||||
fn get_image() -> option<~arc<~image>> {
|
||||
let mut image = none;
|
||||
|
||||
// Do a dance where we swap the ImageHolder out before we can
|
||||
// get the image out of it because we can't alt over it
|
||||
// because holder.get_image() is not pure.
|
||||
if (self.background_image).is_some() {
|
||||
let mut temp = none;
|
||||
temp <-> self.background_image;
|
||||
let holder <- option::unwrap(temp);
|
||||
image = some(holder.get_image());
|
||||
self.background_image = some(holder);
|
||||
}
|
||||
|
||||
ret image;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,11 +70,52 @@ class Box {
|
|||
let appearance: Appearance;
|
||||
|
||||
new(node: Node, kind: BoxKind) {
|
||||
self.appearance = node.read(|n| Appearance(*n.kind));
|
||||
self.tree = tree::empty();
|
||||
self.node = node;
|
||||
self.kind = kind;
|
||||
self.bounds = geometry::zero_rect_au();
|
||||
self.appearance = Appearance();
|
||||
}
|
||||
}
|
||||
|
||||
#[doc="A class to store image data. The image will be loaded once,
|
||||
the first time it is requested, and an arc will be stored. Clones of
|
||||
this arc are given out on demand."]
|
||||
class ImageHolder {
|
||||
// Invariant: at least one of url and image is not none, except
|
||||
// occasionally while get_image is being called
|
||||
let mut url : option<~str>;
|
||||
let mut image : option<arc<~image>>;
|
||||
|
||||
new(-url : ~str) {
|
||||
self.url = some(url);
|
||||
self.image = none;
|
||||
}
|
||||
|
||||
// This function should not be called by two tasks at the same time
|
||||
fn get_image() -> ~arc<~image> {
|
||||
// If this is the first time we've called this function, load
|
||||
// the image and store it for the future
|
||||
if self.image.is_none() {
|
||||
assert self.url.is_some();
|
||||
|
||||
let mut temp = none;
|
||||
temp <-> self.url;
|
||||
let url = option::unwrap(temp);
|
||||
let image = load(url);
|
||||
|
||||
self.image = some(arc(~image));
|
||||
}
|
||||
|
||||
// Temporarily swap out the arc of the image so we can clone
|
||||
// it without breaking purity, then put it back and return the
|
||||
// clone. This is not threadsafe.
|
||||
let mut temp = none;
|
||||
temp <-> self.image;
|
||||
let im_arc = option::unwrap(temp);
|
||||
self.image = some(clone(&im_arc));
|
||||
|
||||
ret ~im_arc;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,6 +138,7 @@ impl NodeTreeReadMethods of tree::ReadMethods<Node> for NTree {
|
|||
}
|
||||
|
||||
enum BTree { BTree }
|
||||
|
||||
impl BoxTreeReadMethods of tree::ReadMethods<@Box> for BTree {
|
||||
fn each_child(node: @Box, f: fn(&&@Box) -> bool) {
|
||||
tree::each_child(self, node, f)
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
export build_display_list;
|
||||
|
||||
import base::{Box, TextBox, BTree, BoxTreeReadMethods};
|
||||
import base::{Box, TextBox, BTree, BoxTreeReadMethods, ImageHolder};
|
||||
import box_builder::box_builder_methods;
|
||||
import dl = display_list;
|
||||
import dom::base::{Text, NodeScope};
|
||||
import dom::rcu::Scope;
|
||||
import either::{left, right};
|
||||
import geom::point::Point2D;
|
||||
import geom::rect::Rect;
|
||||
import geom::size::Size2D;
|
||||
import gfx::geometry::{au, au_to_px, box, px_to_au};
|
||||
import gfx::renderer;
|
||||
import image::base::load;
|
||||
import text::text_layout_methods;
|
||||
import util::color::methods;
|
||||
import util::tree;
|
||||
|
@ -70,8 +72,8 @@ fn box_to_display_items(list: dl::display_list, box: @Box, origin: Point2D<au>)
|
|||
let bounds = Rect(origin, copy box.bounds.size);
|
||||
let col = box.appearance.background_color;
|
||||
|
||||
alt (box.kind, copy box.appearance.background_image) {
|
||||
(TextBox(subbox), _) {
|
||||
alt box.kind {
|
||||
TextBox(subbox) {
|
||||
let run = copy subbox.run;
|
||||
assert run.is_some();
|
||||
list.push(dl::display_item({
|
||||
|
@ -82,17 +84,23 @@ fn box_to_display_items(list: dl::display_list, box: @Box, origin: Point2D<au>)
|
|||
item_type: dl::display_item_text(run.get()),
|
||||
bounds: bounds
|
||||
}));
|
||||
ret;
|
||||
}
|
||||
(_, some(image)) {
|
||||
_ {
|
||||
// Fall through
|
||||
}
|
||||
};
|
||||
|
||||
// Check if there is a background image, if not set the background color.
|
||||
let image = box.appearance.get_image();
|
||||
|
||||
if image.is_some() {
|
||||
let display_item = dl::display_item({
|
||||
item_type: do future::with(*image) |image| {
|
||||
dl::display_item_image(~arc::clone(&*image))
|
||||
},
|
||||
item_type: dl::display_item_image(option::unwrap(image)),
|
||||
bounds: bounds
|
||||
});
|
||||
list.push(display_item);
|
||||
}
|
||||
(_, none) {
|
||||
} else {
|
||||
#debug("Assigning color %? to box with bounds %?", col, bounds);
|
||||
let col = box.appearance.background_color;
|
||||
list.push(dl::display_item({
|
||||
|
@ -100,7 +108,6 @@ fn box_to_display_items(list: dl::display_list, box: @Box, origin: Point2D<au>)
|
|||
bounds: bounds
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ fn Layout(renderer: Renderer) -> Layout {
|
|||
let this_box = node.construct_boxes();
|
||||
this_box.dump();
|
||||
|
||||
this_box.apply_style_for_subtree();
|
||||
this_box.apply_css_style();
|
||||
this_box.reflow(px_to_au(800));
|
||||
|
||||
let dlist = build_display_list(this_box);
|
||||
|
|
|
@ -2,23 +2,25 @@
|
|||
|
||||
import dom::base::{Element, HTMLImageElement, Node};
|
||||
import dom::rcu::ReaderMethods;
|
||||
import either::right;
|
||||
import image::base::load;
|
||||
import base::{Box, BTree, NTree, LayoutData, BoxTreeReadMethods, SpecifiedStyle};
|
||||
import base::{Box, BTree, NTree, LayoutData, BoxTreeReadMethods, SpecifiedStyle, ImageHolder};
|
||||
import style::{default_style_methods, style_methods};
|
||||
|
||||
import future_spawn = future::spawn;
|
||||
import traverse::top_down_traversal;
|
||||
|
||||
trait ApplyStyleBoxMethods {
|
||||
fn apply_style_for_subtree();
|
||||
fn apply_css_style();
|
||||
fn apply_style();
|
||||
}
|
||||
|
||||
#[doc="A wrapper so the function can be passed around by name."]
|
||||
fn apply_style_wrapper(box : @Box) {
|
||||
box.apply_style();
|
||||
}
|
||||
|
||||
impl ApplyStyleBoxMethods of ApplyStyleBoxMethods for @Box {
|
||||
fn apply_style_for_subtree() {
|
||||
self.apply_style();
|
||||
for BTree.each_child(self) |child| {
|
||||
child.apply_style_for_subtree();
|
||||
}
|
||||
fn apply_css_style() {
|
||||
top_down_traversal(self, apply_style_wrapper);
|
||||
}
|
||||
|
||||
#[doc="Applies CSS style to a layout box.
|
||||
|
@ -42,19 +44,13 @@ impl ApplyStyleBoxMethods of ApplyStyleBoxMethods for @Box {
|
|||
|
||||
alt element.kind {
|
||||
~HTMLImageElement(*) {
|
||||
alt element.get_attr(~"src") {
|
||||
some(url) {
|
||||
let url = element.get_attr(~"src");
|
||||
|
||||
if url.is_some() {
|
||||
// FIXME: Some sort of BASE HREF support!
|
||||
// FIXME: Parse URLs!
|
||||
#debug("loading image from %s", url);
|
||||
self.appearance.background_image = some(~do future_spawn |copy url| {
|
||||
~arc::arc(~load(url))
|
||||
});
|
||||
}
|
||||
none {
|
||||
/* Ignore. */
|
||||
}
|
||||
}
|
||||
self.appearance.background_image = some(ImageHolder(option::unwrap(url)))
|
||||
};
|
||||
}
|
||||
_ { /* Ignore. */ }
|
||||
}
|
||||
|
@ -64,4 +60,3 @@ impl ApplyStyleBoxMethods of ApplyStyleBoxMethods for @Box {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import arc::{arc, get, clone};
|
||||
|
||||
import dom::style::{DisplayType, DisBlock, DisInline, DisNone, Stylesheet, Unit};
|
||||
import dom::style::{DisplayType, DisBlock, DisInline, DisNone, Stylesheet, Unit, Auto};
|
||||
import dom::base::{Element, HTMLDivElement, HTMLHeadElement, HTMLImageElement, Node, NodeKind};
|
||||
import dom::base::{Text};
|
||||
import dom::rcu::ReaderMethods;
|
||||
|
@ -22,6 +22,8 @@ type SpecifiedStyle = {mut background_color : option<Color>,
|
|||
trait default_style_methods {
|
||||
fn default_color() -> Color;
|
||||
fn default_display_type() -> DisplayType;
|
||||
fn default_width() -> Unit;
|
||||
fn default_height() -> Unit;
|
||||
}
|
||||
|
||||
#[doc="Default stylesfor various attributes in case they don't get initialized from css selectors"]
|
||||
|
@ -49,6 +51,14 @@ impl default_style_methods of default_style_methods for NodeKind {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn default_width() -> Unit {
|
||||
Auto
|
||||
}
|
||||
|
||||
fn default_height() -> Unit {
|
||||
Auto
|
||||
}
|
||||
}
|
||||
|
||||
#[doc="Create a specified style that can be used to initialize a node before selector matching.
|
||||
|
|
118
src/servo/layout/traverse.rs
Normal file
118
src/servo/layout/traverse.rs
Normal file
|
@ -0,0 +1,118 @@
|
|||
#[doc = "Interface for running tree-based traversals over layout boxes"]
|
||||
|
||||
import base::{Box, BTree, NodeMethods};
|
||||
import intrinsic::tydesc;
|
||||
|
||||
export full_traversal;
|
||||
export top_down_traversal;
|
||||
export bottom_up_traversal;
|
||||
|
||||
// The underlying representation of an @T. We don't actually care
|
||||
// what it is, just that we can transform to and from this
|
||||
// representation to send boxes across task boundaries.
|
||||
type shared_box<T> = {
|
||||
mut refcount : uint,
|
||||
// These are generic unsafe pointers, not just *ints
|
||||
foo : *int,
|
||||
bar : *int,
|
||||
baz : *int,
|
||||
payload : T
|
||||
};
|
||||
|
||||
#[doc="Transform and @ into its underlying representation. The reference count stays constant."]
|
||||
fn unwrap_box(-b : @Box) -> *shared_box<Box> unsafe {
|
||||
let new_box : *shared_box<Box> = unsafe::transmute(b);
|
||||
ret new_box;
|
||||
}
|
||||
|
||||
#[doc="Transform an underlying representation back to an @. The reference count stays constant."]
|
||||
fn rewrap_box(-b : *shared_box<Box>) -> @Box unsafe {
|
||||
let new_box : @Box = unsafe::transmute(b);
|
||||
ret new_box;
|
||||
}
|
||||
|
||||
#[doc="
|
||||
|
||||
Iterate down and then up a tree of layout boxes in parallel and apply
|
||||
the given functions to each box. Each box applies the first function,
|
||||
spawns a task to complete all of its children in parallel, waits for
|
||||
them to finish, and then applies the second function.
|
||||
|
||||
# Arguments
|
||||
|
||||
* `root` - The current top of the tree, the functions will be applied to it and its children.
|
||||
* `top-down` - A function that is applied to each node after it is applied to that node's parent.
|
||||
* `bottom-up` - A function that is applied to each node after it is applied to that node's
|
||||
children
|
||||
"]
|
||||
fn traverse_helper(-root : @Box, -top_down : fn~(@Box), -bottom_up : fn~(@Box)) {
|
||||
top_down(root);
|
||||
|
||||
do listen |ack_chan| {
|
||||
let mut count = 0;
|
||||
|
||||
// For each child we will send it off to another task and then
|
||||
// recurse. It is safe to send these boxes across tasks
|
||||
// because root still holds a reference to the children so
|
||||
// they will not be destroyed from the other task. Also the
|
||||
// current task will block until all of it's children return,
|
||||
// so the original owner of the @-box will not exit while the
|
||||
// children are still live.
|
||||
for BTree.each_child(root) |kid| {
|
||||
count += 1;
|
||||
|
||||
// Unwrap the box so we can send it out of this task
|
||||
let unwrapped = unwrap_box(copy kid);
|
||||
// Hide the box in an option so we can get it across the
|
||||
// task boundary without copying it
|
||||
let swappable : ~mut option<*shared_box<Box>> = ~mut some(unwrapped);
|
||||
|
||||
do task::spawn || {
|
||||
// Get the box out of the option and into the new task
|
||||
let mut swapped_in = none;
|
||||
swapped_in <-> *swappable;
|
||||
|
||||
// Retrieve the original @Box and recurse
|
||||
let new_kid = rewrap_box(option::unwrap(swapped_in));
|
||||
traverse_helper(new_kid, copy top_down, copy bottom_up);
|
||||
|
||||
ack_chan.send(());
|
||||
}
|
||||
}
|
||||
|
||||
// wait for all the children to finish before preceding
|
||||
for count.times() { ack_chan.recv(); }
|
||||
}
|
||||
|
||||
bottom_up(root);
|
||||
}
|
||||
|
||||
#[doc="A noneffectful function to be used if only one pass is required."]
|
||||
fn nop(box : @Box) {
|
||||
ret;
|
||||
}
|
||||
|
||||
#[doc="
|
||||
Iterate in parallel over the boxes in a tree, applying one function
|
||||
to a parent before recursing on its children and one after.
|
||||
"]
|
||||
|
||||
fn full_traversal(+root : @Box, -top_down : fn~(@Box), -bottom_up : fn~(@Box)) {
|
||||
traverse_helper(root, top_down, bottom_up);
|
||||
}
|
||||
|
||||
#[doc="
|
||||
Iterate in parallel over the boxes in a tree, applying the given
|
||||
function to a parent before its children.
|
||||
"]
|
||||
fn top_down_traversal(+root : @Box, -top_down : fn~(@Box)) {
|
||||
traverse_helper(root, top_down, nop);
|
||||
}
|
||||
|
||||
#[doc="
|
||||
Iterate in parallel over the boxes in a tree, applying the given
|
||||
function to a parent after its children.
|
||||
"]
|
||||
fn bottom_up_traversal(+root : @Box, -bottom_up : fn~(@Box)) {
|
||||
traverse_helper(root, nop, bottom_up);
|
||||
}
|
|
@ -59,6 +59,7 @@ mod layout {
|
|||
mod inline;
|
||||
mod layout_task;
|
||||
mod text;
|
||||
mod traverse;
|
||||
}
|
||||
|
||||
mod parser {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue