auto merge of #862 : kmcallister/servo/embed-layout-data, r=jdm

This commit is contained in:
bors-servo 2013-09-03 19:47:07 -07:00
commit 1cdd513c04
8 changed files with 165 additions and 171 deletions

View file

@ -2,10 +2,10 @@
* 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::aux::LayoutAuxMethods;
use layout::incremental::RestyleDamage;
use std::cast::transmute;
use std::cast;
use std::cell::Cell;
use newcss::complete::CompleteSelectResults;
use script::dom::node::{AbstractNode, LayoutView};
@ -27,28 +27,23 @@ impl<'self> NodeUtil<'self> for AbstractNode<LayoutView> {
* stored in a box that can be overwritten
*/
fn get_css_select_results(self) -> &'self CompleteSelectResults {
if !self.has_layout_data() {
fail!(~"style() called on a node without aux data!");
}
match self.layout_data().style {
None => fail!(~"style() called on node without a style!"),
Some(ref style) => unsafe { transmute(style) }
do self.read_layout_data |layout_data| {
match layout_data.style {
None => fail!(~"style() called on node without a style!"),
Some(ref style) => unsafe { cast::transmute_region(style) }
}
}
}
/// Does this node have a computed style yet?
fn have_css_select_results(self) -> bool {
self.has_layout_data() && self.layout_data().style.is_some()
self.read_layout_data(|data| data.style.is_some())
}
/// Update the computed style of an HTML element with a style specified by CSS.
fn set_css_select_results(self, decl: CompleteSelectResults) {
if !self.has_layout_data() {
fail!(~"set_css_select_results() called on a node without aux data!");
}
self.layout_data().style = Some(decl);
let cell = Cell::new(decl);
self.write_layout_data(|data| data.style = Some(cell.take()));
}
/// Get the description of how to account for recent style changes.
@ -62,18 +57,15 @@ impl<'self> NodeUtil<'self> for AbstractNode<LayoutView> {
RestyleDamage::none()
};
if !self.has_layout_data() {
return default;
do self.read_layout_data |layout_data| {
layout_data.restyle_damage
.map(|&x| RestyleDamage::from_int(x))
.unwrap_or_default(default)
}
self.layout_data().restyle_damage.unwrap_or_default(default)
}
/// Set the restyle damage field.
fn set_restyle_damage(self, damage: RestyleDamage) {
if !self.has_layout_data() {
fail!(~"set_restyle_damage() called on a node without aux data!");
}
self.layout_data().restyle_damage = Some(damage);
self.write_layout_data(|data| data.restyle_damage = Some(damage.to_int()));
}
}

View file

@ -4,94 +4,28 @@
//! Code for managing the layout data in the DOM.
use layout::incremental::RestyleDamage;
use gfx::display_list::DisplayList;
use servo_util::range::Range;
use extra::arc::Arc;
use newcss::complete::CompleteSelectResults;
use script::dom::node::{AbstractNode, LayoutView};
use servo_util::tree::TreeNodeRef;
pub struct DisplayBoxes {
display_list: Option<Arc<DisplayList<AbstractNode<()>>>>,
range: Option<Range>,
}
/// Data that layout associates with a node.
pub struct LayoutData {
/// The results of CSS styling for this node.
style: Option<CompleteSelectResults>,
/// Description of how to account for recent style changes.
restyle_damage: Option<RestyleDamage>,
/// The boxes assosiated with this flow.
/// Used for getBoundingClientRect and friends.
boxes: DisplayBoxes,
}
impl LayoutData {
/// Creates new layout data.
pub fn new() -> LayoutData {
LayoutData {
style: None,
restyle_damage: None,
boxes: DisplayBoxes { display_list: None, range: None },
}
}
}
/// Functionality useful for querying the layout-specific data on DOM nodes.
pub trait LayoutAuxMethods {
fn layout_data(self) -> @mut LayoutData;
fn has_layout_data(self) -> bool;
fn set_layout_data(self, data: @mut LayoutData);
fn initialize_layout_data(self) -> Option<@mut LayoutData>;
fn initialize_style_for_subtree(self, refs: &mut ~[@mut LayoutData]);
fn initialize_layout_data(self);
fn initialize_style_for_subtree(self);
}
impl LayoutAuxMethods for AbstractNode<LayoutView> {
fn layout_data(self) -> @mut LayoutData {
unsafe {
self.unsafe_layout_data()
}
}
fn has_layout_data(self) -> bool {
unsafe {
self.unsafe_has_layout_data()
}
}
fn set_layout_data(self, data: @mut LayoutData) {
unsafe {
self.unsafe_set_layout_data(data)
/// Resets layout data and styles for the node.
fn initialize_layout_data(self) {
do self.write_layout_data |data| {
data.boxes.display_list = None;
data.boxes.range = None;
}
}
/// If none exists, creates empty layout data for the node (the reader-auxiliary
/// box in the COW model) and populates it with an empty style object.
fn initialize_layout_data(self) -> Option<@mut LayoutData> {
if self.has_layout_data() {
self.layout_data().boxes.display_list = None;
self.layout_data().boxes.range = None;
None
} else {
let data = @mut LayoutData::new();
self.set_layout_data(data);
Some(data)
/// Resets layout data and styles for a Node tree.
fn initialize_style_for_subtree(self) {
for n in self.traverse_preorder() {
n.initialize_layout_data();
}
}
/// Initializes layout data and styles for a Node tree, if any nodes do not have
/// this data already. Append created layout data to the task's GC roots.
fn initialize_style_for_subtree(self, refs: &mut ~[@mut LayoutData]) {
let _ = for n in self.traverse_preorder() {
match n.initialize_layout_data() {
Some(r) => refs.push(r),
None => {}
}
};
}
}

View file

@ -54,6 +54,17 @@ impl RestyleDamage {
RestyleDamage::all()
}
/// Create a RestyleDamage from the underlying bit field.
/// We would rather not allow this, but some types in script
/// need to store RestyleDamage without depending on this crate.
pub fn from_int(n: int) -> RestyleDamage {
RestyleDamage { bits: n }
}
pub fn to_int(self) -> int {
self.bits
}
pub fn is_empty(self) -> bool {
self.bits == 0
}

View file

@ -7,7 +7,7 @@
use css::matching::MatchMethods;
use css::select::new_css_select_ctx;
use layout::aux::{LayoutData, LayoutAuxMethods};
use layout::aux::LayoutAuxMethods;
use layout::box_builder::LayoutTreeBuilder;
use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder};
@ -59,9 +59,6 @@ struct LayoutTask {
doc_url: Option<Url>,
screen_size: Option<Size2D<Au>>,
/// This is used to root reader data.
layout_refs: ~[@mut LayoutData],
display_list: Option<Arc<DisplayList<AbstractNode<()>>>>,
css_select_ctx: @mut SelectCtx,
@ -123,7 +120,6 @@ impl LayoutTask {
display_list: None,
layout_refs: ~[],
css_select_ctx: @mut new_css_select_ctx(),
profiler_chan: profiler_chan,
}
@ -210,7 +206,7 @@ impl LayoutTask {
//
// FIXME: This is inefficient. We don't need an entire traversal to do this!
do profile(time::LayoutAuxInitCategory, self.profiler_chan.clone()) {
node.initialize_style_for_subtree(&mut self.layout_refs);
node.initialize_style_for_subtree();
}
// Perform CSS selector matching if necessary.
@ -326,15 +322,14 @@ impl LayoutTask {
let node: AbstractNode<LayoutView> = unsafe {
transmute(display_list.get().list[i].base().extra)
};
assert!(node.has_layout_data(), "Node has display item but no layout data");
let layout_data = node.layout_data();
layout_data.boxes.display_list = Some(display_list.clone());
do node.write_layout_data |layout_data| {
layout_data.boxes.display_list = Some(display_list.clone());
if layout_data.boxes.range.is_none() {
debug!("Creating initial range for node");
layout_data.boxes.range = Some(Range::new(i,1));
} else {
if layout_data.boxes.range.is_none() {
debug!("Creating initial range for node");
layout_data.boxes.range = Some(Range::new(i,1));
} else {
debug!("Appending item to range");
unsafe {
let old_node: AbstractNode<()> = transmute(node);
@ -343,6 +338,7 @@ impl LayoutTask {
}
layout_data.boxes.range.unwrap().extend_by(1);
}
}
}
@ -376,28 +372,30 @@ impl LayoutTask {
transmute(node)
};
let response = match (node.layout_data().boxes.display_list.clone(), node.layout_data().boxes.range) {
(Some(display_list), Some(range)) => {
let mut rect: Option<Rect<Au>> = None;
for i in range.eachi() {
rect = match rect {
Some(acc) => Some(acc.union(&display_list.get().list[i].bounds())),
None => Some(display_list.get().list[i].bounds())
let response = do node.read_layout_data |layout_data| {
match (layout_data.boxes.display_list.clone(), layout_data.boxes.range) {
(Some(display_list), Some(range)) => {
let mut rect: Option<Rect<Au>> = None;
for i in range.eachi() {
rect = match rect {
Some(acc) => Some(acc.union(&display_list.get().list[i].bounds())),
None => Some(display_list.get().list[i].bounds())
}
}
match rect {
None => {
error!("no boxes for node");
Err(())
}
Some(rect) => Ok(ContentBoxResponse(rect))
}
}
match rect {
None => {
error!("no boxes for node");
Err(())
}
Some(rect) => Ok(ContentBoxResponse(rect))
_ => {
error!("no display list present");
Err(())
}
}
_ => {
error!("no display list present");
Err(())
}
};
reply_chan.send(response)
@ -408,16 +406,18 @@ impl LayoutTask {
transmute(node)
};
let response = match (node.layout_data().boxes.display_list.clone(), node.layout_data().boxes.range) {
(Some(display_list), Some(range)) => {
let mut boxes = ~[];
for i in range.eachi() {
boxes.push(display_list.get().list[i].bounds());
}
let response = do node.read_layout_data |layout_data| {
match (layout_data.boxes.display_list.clone(), layout_data.boxes.range) {
(Some(display_list), Some(range)) => {
let mut boxes = ~[];
for i in range.eachi() {
boxes.push(display_list.get().list[i].bounds());
}
Ok(ContentBoxesResponse(boxes))
Ok(ContentBoxesResponse(boxes))
}
_ => Err(()),
}
_ => Err(()),
};
reply_chan.send(response)

View file

@ -10,7 +10,6 @@ use script_task::page_from_context;
use std::libc::c_uint;
use std::cast;
use std::cell::Cell;
use std::hashmap::HashMap;
use std::libc;
use std::ptr;
@ -30,8 +29,7 @@ use js::jsapi::{JS_NewStringCopyN, JS_DefineFunctions, JS_DefineProperty};
use js::jsapi::{JS_ValueToString, JS_GetReservedSlot, JS_SetReservedSlot};
use js::jsapi::{JSContext, JSObject, JSBool, jsid, JSClass, JSNative};
use js::jsapi::{JSFunctionSpec, JSPropertySpec, JSVal, JSPropertyDescriptor};
use js::jsapi::{JSFreeOp, JSTracer};
use js::jsapi::{JSPropertyOp, JSStrictPropertyOp, JSEnumerateOp, JSResolveOp, JSConvertOp};
use js::jsapi::{JSPropertyOp, JSStrictPropertyOp};
use js::jsfriendapi::bindgen::JS_NewObjectWithUniqueType;
use js::rust::Compartment;
use js::{JSPROP_ENUMERATE, JSVAL_NULL};

View file

@ -18,10 +18,14 @@ use dom::text::Text;
use std::cast;
use std::cast::transmute;
use std::libc::c_void;
use extra::arc::Arc;
use js::jsapi::{JSObject, JSContext};
use js::rust::Compartment;
use netsurfcss::util::VoidPtrLike;
use newcss::complete::CompleteSelectResults;
use servo_util::tree::{TreeNode, TreeNodeRef};
use servo_util::range::Range;
use gfx::display_list::DisplayList;
//
// The basic Node structure
@ -56,7 +60,7 @@ pub struct AbstractNodeChildrenIterator<View> {
///
/// `View` describes extra data associated with this node that this task has access to. For
/// the script task, this is the unit type `()`. For the layout task, this is
/// `layout::aux::LayoutData`.
/// `LayoutData`.
pub struct Node<View> {
/// The JavaScript wrapper for this node.
wrapper: WrapperCache,
@ -85,7 +89,7 @@ pub struct Node<View> {
owner_doc: Option<AbstractDocument>,
/// Layout information. Only the layout task may touch this data.
priv layout_data: Option<@mut ()>
priv layout_data: LayoutData,
}
/// The different types of nodes.
@ -172,31 +176,6 @@ impl<'self, View> AbstractNode<View> {
}
}
/// Returns the layout data, unsafely cast to whatever type layout wishes. Only layout is
/// allowed to call this. This is wildly unsafe and is therefore marked as such.
pub unsafe fn unsafe_layout_data<T>(self) -> @mut T {
do self.with_base |base| {
transmute(base.layout_data.unwrap())
}
}
/// Returns true if this node has layout data and false otherwise.
pub unsafe fn unsafe_has_layout_data(self) -> bool {
do self.with_base |base| {
base.layout_data.is_some()
}
}
/// Sets the layout data, unsafely casting the type as layout wishes. Only layout is allowed
/// to call this. This is wildly unsafe and is therefore marked as such.
pub unsafe fn unsafe_set_layout_data<T>(self, data: @mut T) {
// Don't decrement the refcount on data, since we're giving it to the
// base structure.
cast::forget(data);
do self.with_mut_base |base| {
base.layout_data = Some(transmute(data))
}
}
// Convenience accessors
/// Returns the type ID of this node. Fails if this node is borrowed mutably.
@ -435,7 +414,7 @@ impl Node<ScriptView> {
owner_doc: None,
layout_data: None,
layout_data: LayoutData::new(),
}
}
@ -631,3 +610,52 @@ impl BindingObject for Node<ScriptView> {
}
}
// This stuff is notionally private to layout, but we put it here because it needs
// to be stored in a Node, and we can't have cross-crate cyclic dependencies.
pub struct DisplayBoxes {
display_list: Option<Arc<DisplayList<AbstractNode<()>>>>,
range: Option<Range>,
}
/// Data that layout associates with a node.
pub struct LayoutData {
/// The results of CSS styling for this node.
style: Option<CompleteSelectResults>,
/// 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 {
style: None,
restyle_damage: None,
boxes: DisplayBoxes { display_list: None, range: None },
}
}
}
impl AbstractNode<LayoutView> {
// These accessors take a continuation rather than returning a reference, because
// 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
// necessary.
pub fn read_layout_data<R>(self, blk: &fn(data: &LayoutData) -> R) -> R {
do self.with_base |b| {
blk(&b.layout_data)
}
}
pub fn write_layout_data<R>(self, blk: &fn(data: &mut LayoutData) -> R) -> R {
do self.with_mut_base |b| {
blk(&mut b.layout_data)
}
}
}

View file

@ -0,0 +1,31 @@
/* 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 std::io;
use std::vec::raw::buf_as_slice;
use std::cast::transmute;
use std::sys::size_of;
fn hexdump_slice(buf: &[u8]) {
let stderr = io::stderr();
stderr.write_str(" ");
for (i, &v) in buf.iter().enumerate() {
stderr.write_str(fmt!("%02X ", v as uint));
match i % 16 {
15 => stderr.write_str("\n "),
7 => stderr.write_str(" "),
_ => ()
}
stderr.flush();
}
stderr.write_char('\n');
}
pub fn hexdump<T>(obj: &T) {
unsafe {
let buf: *u8 = transmute(obj);
debug!("dumping at %p", buf);
buf_as_slice(buf, size_of::<T>(), hexdump_slice);
}
}

View file

@ -16,4 +16,4 @@ pub mod time;
pub mod tree;
pub mod url;
pub mod vec;
pub mod debug;