Use Any for the layout data.

Breaks the dependency between `gfx` and `script`, which is nice.
This commit is contained in:
Patrick Walton 2013-11-08 22:45:40 -08:00
parent bdc7e984eb
commit 6ff7b4a6a6
8 changed files with 207 additions and 154 deletions

View file

@ -226,14 +226,14 @@ DONE_gfx = $(B)src/components/gfx/libgfx.dummy
DEPS_gfx = $(CRATE_gfx) $(SRC_gfx) $(DONE_SUBMODULES) $(DONE_util) $(DONE_style) $(DONE_net) $(DONE_msg) DEPS_gfx = $(CRATE_gfx) $(SRC_gfx) $(DONE_SUBMODULES) $(DONE_util) $(DONE_style) $(DONE_net) $(DONE_msg)
RFLAGS_script = $(strip $(CFG_RUSTC_FLAGS)) $(addprefix -L $(B)src/,$(DEPS_SUBMODULES)) -L $(B)src/components/util -L $(B)src/components/style -L $(B)src/components/net -L $(B)src/components/gfx -L $(B)src/components/msg RFLAGS_script = $(strip $(CFG_RUSTC_FLAGS)) $(addprefix -L $(B)src/,$(DEPS_SUBMODULES)) -L $(B)src/components/util -L $(B)src/components/style -L $(B)src/components/net -L $(B)src/components/msg
WEBIDL_script = $(call rwildcard,$(S)src/components/script/,*.webidl) WEBIDL_script = $(call rwildcard,$(S)src/components/script/,*.webidl)
AUTOGEN_SRC_script = $(patsubst %.webidl, %Binding.rs, $(WEBIDL_script)) AUTOGEN_SRC_script = $(patsubst %.webidl, %Binding.rs, $(WEBIDL_script))
SRC_script = $(call rwildcard,$(S)src/components/script/,*.rs) $(AUTOGEN_SRC_script) SRC_script = $(call rwildcard,$(S)src/components/script/,*.rs) $(AUTOGEN_SRC_script)
CRATE_script = $(S)src/components/script/script.rc CRATE_script = $(S)src/components/script/script.rc
DONE_script = $(B)src/components/script/libscript.dummy DONE_script = $(B)src/components/script/libscript.dummy
DEPS_script = $(CRATE_script) $(SRC_script) $(DONE_SUBMODULES) $(DONE_util) $(DONE_style) $(DONE_net) $(DONE_gfx) $(DONE_msg) DEPS_script = $(CRATE_script) $(SRC_script) $(DONE_SUBMODULES) $(DONE_util) $(DONE_style) $(DONE_net) $(DONE_msg)
RFLAGS_style = $(strip $(CFG_RUSTC_FLAGS)) $(addprefix -L $(B)src/,$(DEPS_SUBMODULES)) -L $(B)src/components/util RFLAGS_style = $(strip $(CFG_RUSTC_FLAGS)) $(addprefix -L $(B)src/,$(DEPS_SUBMODULES)) -L $(B)src/components/util
MAKO_ZIP = $(S)src/components/style/Mako-0.8.1.zip MAKO_ZIP = $(S)src/components/style/Mako-0.8.1.zip

View file

@ -14,6 +14,7 @@ use extra::arc::RWArc;
use css::node_style::StyledNode; use css::node_style::StyledNode;
use css::node_util::NodeUtil; use css::node_util::NodeUtil;
use layout::incremental; use layout::incremental;
use layout::util::LayoutDataAccess;
use script::dom::node::{AbstractNode, LayoutView}; use script::dom::node::{AbstractNode, LayoutView};
use style::Stylist; use style::Stylist;
@ -37,10 +38,7 @@ impl MatchMethods for AbstractNode<LayoutView> {
}; };
stylist.get_applicable_declarations(self, style_attribute, None) stylist.get_applicable_declarations(self, style_attribute, None)
}; };
let cell = Cell::new(applicable_declarations); self.layout_data().applicable_declarations.set(applicable_declarations)
do self.write_layout_data |data| {
data.applicable_declarations = cell.take();
}
} }
fn match_subtree(&self, stylist: RWArc<Stylist>) { fn match_subtree(&self, stylist: RWArc<Stylist>) {
let num_tasks = default_sched_threads() * 2; let num_tasks = default_sched_threads() * 2;
@ -84,22 +82,21 @@ impl MatchMethods for AbstractNode<LayoutView> {
Some(parent) => Some(parent.style()), Some(parent) => Some(parent.style()),
None => None None => None
}; };
let computed_values = do self.read_layout_data |data| {
cascade(data.applicable_declarations, parent_style) let layout_data = self.layout_data();
}; let computed_values = cascade(*layout_data.applicable_declarations.borrow().ptr,
let cell = Cell::new(computed_values); parent_style);
do self.write_layout_data |data| { let style = layout_data.style.mutate();
let style = cell.take(); match *style.ptr {
// If there was an existing style, compute the damage that
// incremental layout will need to fix.
match data.style {
None => (), None => (),
Some(ref previous_style) => self.set_restyle_damage( Some(ref previous_style) => {
incremental::compute_damage(previous_style, &style)) self.set_restyle_damage(incremental::compute_damage(previous_style,
} &computed_values))
data.style = Some(style);
} }
} }
*style.ptr = Some(computed_values)
}
fn cascade_subtree(&self, parent: Option<AbstractNode<LayoutView>>) { fn cascade_subtree(&self, parent: Option<AbstractNode<LayoutView>>) {
self.cascade_node(parent); self.cascade_node(parent);

View file

@ -3,14 +3,13 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use layout::incremental::RestyleDamage; use layout::incremental::RestyleDamage;
use layout::util::LayoutDataAccess;
use std::cast; use std::cast;
use std::cell::Cell;
use style::ComputedValues; use style::ComputedValues;
use script::dom::node::{AbstractNode, LayoutView}; use script::dom::node::{AbstractNode, LayoutView};
use servo_util::tree::TreeNodeRef; use servo_util::tree::TreeNodeRef;
pub trait NodeUtil<'self> { pub trait NodeUtil<'self> {
fn get_css_select_results(self) -> &'self ComputedValues; fn get_css_select_results(self) -> &'self ComputedValues;
fn set_css_select_results(self, decl: ComputedValues); fn set_css_select_results(self, decl: ComputedValues);
@ -29,23 +28,21 @@ impl<'self> NodeUtil<'self> for AbstractNode<LayoutView> {
* stored in a box that can be overwritten * stored in a box that can be overwritten
*/ */
fn get_css_select_results(self) -> &'self ComputedValues { fn get_css_select_results(self) -> &'self ComputedValues {
do self.read_layout_data |layout_data| { let layout_data = self.layout_data();
match layout_data.style { match *layout_data.style.borrow().ptr {
None => fail!(~"style() called on node without a style!"), None => fail!(~"style() called on node without a style!"),
Some(ref style) => unsafe { cast::transmute_region(style) } Some(ref style) => unsafe { cast::transmute_region(style) }
} }
} }
}
/// Does this node have a computed style yet? /// Does this node have a computed style yet?
fn have_css_select_results(self) -> bool { fn have_css_select_results(self) -> bool {
self.read_layout_data(|data| data.style.is_some()) self.layout_data().style.borrow().ptr.is_some()
} }
/// Update the computed style of an HTML element with a style specified by CSS. /// Update the computed style of an HTML element with a style specified by CSS.
fn set_css_select_results(self, decl: ComputedValues) { fn set_css_select_results(self, decl: ComputedValues) {
let cell = Cell::new(decl); *self.layout_data().style.mutate().ptr = Some(decl)
self.write_layout_data(|data| data.style = Some(cell.take()));
} }
/// Get the description of how to account for recent style changes. /// Get the description of how to account for recent style changes.
@ -59,15 +56,17 @@ impl<'self> NodeUtil<'self> for AbstractNode<LayoutView> {
RestyleDamage::none() RestyleDamage::none()
}; };
do self.read_layout_data |layout_data| { self.layout_data()
layout_data.restyle_damage .restyle_damage
.borrow()
.ptr
.map(|x| RestyleDamage::from_int(x)) .map(|x| RestyleDamage::from_int(x))
.unwrap_or(default) .unwrap_or(default)
} }
}
/// Set the restyle damage field. /// Set the restyle damage field.
fn set_restyle_damage(self, damage: RestyleDamage) { fn set_restyle_damage(self, damage: RestyleDamage) {
self.write_layout_data(|data| data.restyle_damage = Some(damage.to_int())); *self.layout_data().restyle_damage.mutate().ptr = Some(damage.to_int())
} }
} }

View file

@ -4,8 +4,11 @@
//! Code for managing the layout data in the DOM. //! Code for managing the layout data in the DOM.
use layout::util::{DisplayBoxes, LayoutData, LayoutDataAccess};
use script::dom::node::{AbstractNode, LayoutView}; use script::dom::node::{AbstractNode, LayoutView};
use servo_util::tree::TreeNodeRef; use servo_util::tree::TreeNodeRef;
use std::cast;
/// Functionality useful for querying the layout-specific data on DOM nodes. /// Functionality useful for querying the layout-specific data on DOM nodes.
pub trait LayoutAuxMethods { pub trait LayoutAuxMethods {
@ -15,10 +18,16 @@ pub trait LayoutAuxMethods {
impl LayoutAuxMethods for AbstractNode<LayoutView> { impl LayoutAuxMethods for AbstractNode<LayoutView> {
/// Resets layout data and styles for the node. /// Resets layout data and styles for the node.
///
/// FIXME(pcwalton): Do this as part of box building instead of in a traversal.
fn initialize_layout_data(self) { fn initialize_layout_data(self) {
do self.write_layout_data |data| { unsafe {
data.boxes.display_list = None; let node = cast::transmute_mut(self.node());
data.boxes.range = None; if node.layout_data.is_none() {
node.layout_data = Some(~LayoutData::new() as ~Any)
} else {
self.layout_data().boxes.set(DisplayBoxes::init());
}
} }
} }

View file

@ -15,10 +15,11 @@ use layout::flow::{FlowContext, ImmutableFlowUtils, MutableFlowUtils, PreorderFl
use layout::flow::{PostorderFlowTraversal}; use layout::flow::{PostorderFlowTraversal};
use layout::flow; use layout::flow;
use layout::incremental::{RestyleDamage, BubbleWidths}; use layout::incremental::{RestyleDamage, BubbleWidths};
use layout::util::LayoutDataAccess;
use std::cast::transmute; use std::cast::transmute;
use std::cell::Cell; use std::cell::Cell;
use std::comm::{Port}; use std::comm::Port;
use std::task; use std::task;
use extra::arc::{Arc, RWArc}; use extra::arc::{Arc, RWArc};
use geom::point::Point2D; use geom::point::Point2D;
@ -419,12 +420,14 @@ impl LayoutTask {
transmute(display_list.get().list[i].base().extra) transmute(display_list.get().list[i].base().extra)
}; };
do node.write_layout_data |layout_data| { // FIXME(pcwalton): Why are we cloning the display list here?!
layout_data.boxes.display_list = Some(display_list.clone()); let layout_data = node.layout_data();
let boxes = layout_data.boxes.mutate();
boxes.ptr.display_list = Some(display_list.clone());
if layout_data.boxes.range.is_none() { if boxes.ptr.range.is_none() {
debug!("Creating initial range for node"); debug!("Creating initial range for node");
layout_data.boxes.range = Some(Range::new(i,1)); boxes.ptr.range = Some(Range::new(i,1));
} else { } else {
debug!("Appending item to range"); debug!("Appending item to range");
unsafe { unsafe {
@ -433,8 +436,7 @@ impl LayoutTask {
"Non-contiguous arrangement of display items"); "Non-contiguous arrangement of display items");
} }
layout_data.boxes.range.unwrap().extend_by(1); boxes.ptr.range.unwrap().extend_by(1);
}
} }
} }
@ -469,8 +471,10 @@ impl LayoutTask {
}; };
fn box_for_node(node: AbstractNode<LayoutView>) -> Option<Rect<Au>> { fn box_for_node(node: AbstractNode<LayoutView>) -> Option<Rect<Au>> {
do node.read_layout_data |layout_data| { // FIXME(pcwalton): Why are we cloning the display list here?!
match (layout_data.boxes.display_list.clone(), layout_data.boxes.range) { let boxes = node.layout_data().boxes.borrow();
let boxes = boxes.ptr;
match (boxes.display_list.clone(), boxes.range) {
(Some(display_list), Some(range)) => { (Some(display_list), Some(range)) => {
let mut rect: Option<Rect<Au>> = None; let mut rect: Option<Rect<Au>> = None;
for i in range.eachi() { for i in range.eachi() {
@ -499,7 +503,6 @@ impl LayoutTask {
} }
} }
} }
}
let rect = box_for_node(node).unwrap_or(Rect(Point2D(Au(0), Au(0)), let rect = box_for_node(node).unwrap_or(Rect(Point2D(Au(0), Au(0)),
Size2D(Au(0), Au(0)))); Size2D(Au(0), Au(0))));
@ -511,25 +514,23 @@ impl LayoutTask {
transmute(node) transmute(node)
}; };
fn boxes_for_node(node: AbstractNode<LayoutView>, fn boxes_for_node(node: AbstractNode<LayoutView>, mut box_accumulator: ~[Rect<Au>])
boxes: ~[Rect<Au>]) -> ~[Rect<Au>] { -> ~[Rect<Au>] {
let boxes = Cell::new(boxes); let boxes = node.layout_data().boxes.borrow();
do node.read_layout_data |layout_data| { let boxes = boxes.ptr;
let mut boxes = boxes.take(); match (boxes.display_list.clone(), boxes.range) {
match (layout_data.boxes.display_list.clone(), layout_data.boxes.range) {
(Some(display_list), Some(range)) => { (Some(display_list), Some(range)) => {
for i in range.eachi() { for i in range.eachi() {
boxes.push(display_list.get().list[i].bounds()); box_accumulator.push(display_list.get().list[i].bounds());
} }
} }
_ => { _ => {
for child in node.children() { for child in node.children() {
boxes = boxes_for_node(child, boxes); box_accumulator = boxes_for_node(child, box_accumulator);
} }
} }
} }
boxes box_accumulator
}
} }
let mut boxes = ~[]; let mut boxes = ~[];

View file

@ -3,12 +3,34 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use layout::box::{RenderBox, RenderBoxUtils}; use layout::box::{RenderBox, RenderBoxUtils};
use extra::arc::Arc;
use gfx::display_list::DisplayList;
use script::dom::node::{AbstractNode, LayoutView}; use script::dom::node::{AbstractNode, LayoutView};
use servo_util::range::Range; use servo_util::range::Range;
use servo_util::slot::Slot;
use servo_util::tree::TreeNodeRef;
use std::any::AnyRefExt;
use std::iter::Enumerate; use std::iter::Enumerate;
use std::vec::VecIterator; use std::vec::VecIterator;
use style::{ComputedValues, PropertyDeclaration};
/// The boxes associated with a node.
pub struct DisplayBoxes {
display_list: Option<Arc<DisplayList<AbstractNode<()>>>>,
range: Option<Range>,
}
impl DisplayBoxes {
pub fn init() -> DisplayBoxes {
DisplayBoxes {
display_list: None,
range: None,
}
}
}
/// A range of nodes.
pub struct NodeRange { pub struct NodeRange {
node: AbstractNode<LayoutView>, node: AbstractNode<LayoutView>,
range: Range, range: Range,
@ -16,7 +38,10 @@ pub struct NodeRange {
impl NodeRange { impl NodeRange {
pub fn new(node: AbstractNode<LayoutView>, range: &Range) -> NodeRange { pub fn new(node: AbstractNode<LayoutView>, range: &Range) -> NodeRange {
NodeRange { node: node, range: (*range).clone() } NodeRange {
node: node,
range: (*range).clone()
}
} }
} }
@ -111,3 +136,51 @@ impl ElementMapping {
debug!("----------------------------------"); debug!("----------------------------------");
} }
} }
/// Data that layout associates with a node.
pub struct LayoutData {
/// The results of CSS matching for this node.
applicable_declarations: Slot<~[Arc<~[PropertyDeclaration]>]>,
/// The results of CSS styling for this node.
style: Slot<Option<ComputedValues>>,
/// Description of how to account for recent style changes.
restyle_damage: Slot<Option<int>>,
/// The boxes assosiated with this flow.
/// Used for getBoundingClientRect and friends.
boxes: Slot<DisplayBoxes>,
}
impl LayoutData {
/// Creates new layout data.
pub fn new() -> LayoutData {
LayoutData {
applicable_declarations: Slot::init(~[]),
style: Slot::init(None),
restyle_damage: Slot::init(None),
boxes: Slot::init(DisplayBoxes::init()),
}
}
}
// This serves as a static assertion that layout data remains sendable. If this is not done, then
// we can have memory unsafety, which usually manifests as shutdown crashes.
fn assert_is_sendable<T:Send>(_: T) {}
fn assert_layout_data_is_sendable() {
assert_is_sendable(LayoutData::new())
}
/// A trait that allows access to the layout data of a DOM node.
pub trait LayoutDataAccess {
fn layout_data<'a>(&'a self) -> &'a LayoutData;
}
impl LayoutDataAccess for AbstractNode<LayoutView> {
#[inline(always)]
fn layout_data<'a>(&'a self) -> &'a LayoutData {
self.node().layout_data.as_ref().unwrap().as_ref().unwrap()
}
}

View file

@ -21,12 +21,8 @@ use dom::text::Text;
use std::cast; use std::cast;
use std::cast::transmute; use std::cast::transmute;
use std::unstable::raw::Box; use std::unstable::raw::Box;
use extra::arc::Arc;
use js::jsapi::{JSObject, JSContext}; use js::jsapi::{JSObject, JSContext};
use style::{ComputedValues, PropertyDeclaration};
use servo_util::tree::{TreeNode, TreeNodeRef, TreeNodeRefAsElement}; use servo_util::tree::{TreeNode, TreeNodeRef, TreeNodeRefAsElement};
use servo_util::range::Range;
use gfx::display_list::DisplayList;
// //
// The basic Node structure // The basic Node structure
@ -93,7 +89,7 @@ pub struct Node<View> {
child_list: Option<@mut NodeList>, child_list: Option<@mut NodeList>,
/// Layout information. Only the layout task may touch this data. /// Layout information. Only the layout task may touch this data.
priv layout_data: LayoutData, layout_data: Option<~Any>,
} }
/// The different types of nodes. /// The different types of nodes.
@ -543,7 +539,7 @@ impl Node<ScriptView> {
owner_doc: doc, owner_doc: doc,
child_list: None, child_list: None,
layout_data: LayoutData::new(), layout_data: None,
} }
} }
} }
@ -1058,62 +1054,41 @@ impl Reflectable for Node<ScriptView> {
} }
} }
// This stuff is notionally private to layout, but we put it here because it needs /// A bottom-up, parallelizable traversal.
// to be stored in a Node, and we can't have cross-crate cyclic dependencies. pub trait PostorderNodeTraversal {
/// The operation to perform. Return true to continue or false to stop.
fn process(&mut self, node: AbstractNode<LayoutView>) -> bool;
pub struct DisplayBoxes { /// Returns true if this node should be pruned. If this returns true, we skip the operation
display_list: Option<Arc<DisplayList<AbstractNode<()>>>>, /// entirely and do not process any descendant nodes. This is called *before* child nodes are
range: Option<Range>, /// visited. The default implementation never prunes any nodes.
fn should_prune(&mut self, _node: AbstractNode<LayoutView>) -> bool {
false
} }
/// Data that layout associates with a node.
pub struct LayoutData {
/// The results of CSS matching for this node.
applicable_declarations: ~[Arc<~[PropertyDeclaration]>],
/// The results of CSS styling for this node.
style: Option<ComputedValues>,
/// Description of how to account for recent style changes.
restyle_damage: Option<int>,
/// The boxes assosiated with this flow.
/// Used for getBoundingClientRect and friends.
boxes: DisplayBoxes,
}
impl LayoutData {
/// Creates new layout data.
pub fn new() -> LayoutData {
LayoutData {
applicable_declarations: ~[],
style: None,
restyle_damage: None,
boxes: DisplayBoxes {
display_list: None,
range: None,
},
}
}
}
// This serves as a static assertion that layout data remains sendable. If this is not done, then
// we can have memory unsafety, which usually manifests as shutdown crashes.
fn assert_is_sendable<T:Send>(_: T) {}
fn assert_layout_data_is_sendable() {
assert_is_sendable(LayoutData::new())
} }
impl AbstractNode<LayoutView> { impl AbstractNode<LayoutView> {
// These accessors take a continuation rather than returning a reference, because /// Traverses the tree in postorder.
// an AbstractNode doesn't have a lifetime parameter relating to the underlying ///
// Node. Also this makes it easier to switch to RWArc if we decide that is /// TODO(pcwalton): Offer a parallel version with a compatible API.
// necessary. pub fn traverse_postorder<T:PostorderNodeTraversal>(self, traversal: &mut T) -> bool {
pub fn read_layout_data<R>(self, blk: &fn(data: &LayoutData) -> R) -> R { if traversal.should_prune(self) {
blk(&self.node().layout_data) return true
} }
pub fn write_layout_data<R>(self, blk: &fn(data: &mut LayoutData) -> R) -> R { let mut opt_kid = self.first_child();
blk(&mut self.mut_node().layout_data) loop {
match opt_kid {
None => break,
Some(kid) => {
if !kid.traverse_postorder(traversal) {
return false
}
opt_kid = kid.next_sibling()
}
}
}
traversal.process(self)
} }
} }

View file

@ -14,7 +14,6 @@
#[feature(globs, macro_rules, struct_variant, managed_boxes)]; #[feature(globs, macro_rules, struct_variant, managed_boxes)];
extern mod geom; extern mod geom;
extern mod gfx (name = "gfx");
extern mod hubbub; extern mod hubbub;
extern mod js; extern mod js;
extern mod servo_net (name = "net"); extern mod servo_net (name = "net");