mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +01:00
Embed layout data directly in Node
This eliminates layout_data: Option<@mut ()> and the unsafe casting around it, which was causing crashes on exit. Fixes #762.
This commit is contained in:
parent
fc3afc5059
commit
c22547a4ef
5 changed files with 152 additions and 148 deletions
|
@ -2,10 +2,10 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use layout::aux::LayoutAuxMethods;
|
|
||||||
use layout::incremental::RestyleDamage;
|
use layout::incremental::RestyleDamage;
|
||||||
|
|
||||||
use std::cast::transmute;
|
use std::cast;
|
||||||
|
use std::cell::Cell;
|
||||||
use newcss::complete::CompleteSelectResults;
|
use newcss::complete::CompleteSelectResults;
|
||||||
use script::dom::node::{AbstractNode, LayoutView};
|
use script::dom::node::{AbstractNode, LayoutView};
|
||||||
|
|
||||||
|
@ -31,15 +31,20 @@ impl<'self> NodeUtil<'self> for AbstractNode<LayoutView> {
|
||||||
fail!(~"style() called on a node without aux data!");
|
fail!(~"style() called on a node without aux data!");
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.layout_data().style {
|
do self.read_layout_data |layout_data| {
|
||||||
|
match layout_data.style {
|
||||||
None => fail!(~"style() called on node without a style!"),
|
None => fail!(~"style() called on node without a style!"),
|
||||||
Some(ref style) => unsafe { transmute(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.has_layout_data() && self.layout_data().style.is_some()
|
if !self.has_layout_data() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
self.read_layout_data(|data| data.style.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.
|
||||||
|
@ -48,7 +53,8 @@ impl<'self> NodeUtil<'self> for AbstractNode<LayoutView> {
|
||||||
fail!(~"set_css_select_results() called on a node without aux 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.
|
/// Get the description of how to account for recent style changes.
|
||||||
|
@ -65,7 +71,11 @@ impl<'self> NodeUtil<'self> for AbstractNode<LayoutView> {
|
||||||
if !self.has_layout_data() {
|
if !self.has_layout_data() {
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
self.layout_data().restyle_damage.unwrap_or_default(default)
|
do self.read_layout_data |layout_data| {
|
||||||
|
layout_data.restyle_damage
|
||||||
|
.map(|&x| RestyleDamage::from_int(x))
|
||||||
|
.unwrap_or_default(default)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the restyle damage field.
|
/// Set the restyle damage field.
|
||||||
|
@ -74,6 +84,6 @@ impl<'self> NodeUtil<'self> for AbstractNode<LayoutView> {
|
||||||
fail!(~"set_restyle_damage() called on a node without aux 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()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,94 +4,34 @@
|
||||||
|
|
||||||
//! Code for managing the layout data in the DOM.
|
//! Code for managing the layout data in the DOM.
|
||||||
|
|
||||||
use layout::incremental::RestyleDamage;
|
use script::dom::node::{AbstractNode, LayoutView, LayoutData};
|
||||||
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;
|
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.
|
/// Functionality useful for querying the layout-specific data on DOM nodes.
|
||||||
pub trait LayoutAuxMethods {
|
pub trait LayoutAuxMethods {
|
||||||
fn layout_data(self) -> @mut LayoutData;
|
fn initialize_layout_data(self);
|
||||||
fn has_layout_data(self) -> bool;
|
fn initialize_style_for_subtree(self);
|
||||||
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]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LayoutAuxMethods for AbstractNode<LayoutView> {
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If none exists, creates empty layout data for the node (the reader-auxiliary
|
/// 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.
|
/// box in the COW model) and populates it with an empty style object.
|
||||||
fn initialize_layout_data(self) -> Option<@mut LayoutData> {
|
fn initialize_layout_data(self) {
|
||||||
if self.has_layout_data() {
|
if self.has_layout_data() {
|
||||||
self.layout_data().boxes.display_list = None;
|
do self.write_layout_data |data| {
|
||||||
self.layout_data().boxes.range = None;
|
data.boxes.display_list = None;
|
||||||
None
|
data.boxes.range = None;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let data = @mut LayoutData::new();
|
self.set_layout_data(LayoutData::new());
|
||||||
self.set_layout_data(data);
|
|
||||||
Some(data)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initializes layout data and styles for a Node tree, if any nodes do not have
|
/// 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.
|
/// this data already.
|
||||||
fn initialize_style_for_subtree(self, refs: &mut ~[@mut LayoutData]) {
|
fn initialize_style_for_subtree(self) {
|
||||||
let _ = for n in self.traverse_preorder() {
|
for n in self.traverse_preorder() {
|
||||||
match n.initialize_layout_data() {
|
n.initialize_layout_data();
|
||||||
Some(r) => refs.push(r),
|
}
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,17 @@ impl RestyleDamage {
|
||||||
RestyleDamage::all()
|
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 {
|
pub fn is_empty(self) -> bool {
|
||||||
self.bits == 0
|
self.bits == 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
use css::matching::MatchMethods;
|
use css::matching::MatchMethods;
|
||||||
use css::select::new_css_select_ctx;
|
use css::select::new_css_select_ctx;
|
||||||
use layout::aux::{LayoutData, LayoutAuxMethods};
|
use layout::aux::LayoutAuxMethods;
|
||||||
use layout::box_builder::LayoutTreeBuilder;
|
use layout::box_builder::LayoutTreeBuilder;
|
||||||
use layout::context::LayoutContext;
|
use layout::context::LayoutContext;
|
||||||
use layout::display_list_builder::{DisplayListBuilder};
|
use layout::display_list_builder::{DisplayListBuilder};
|
||||||
|
@ -59,9 +59,6 @@ struct LayoutTask {
|
||||||
doc_url: Option<Url>,
|
doc_url: Option<Url>,
|
||||||
screen_size: Option<Size2D<Au>>,
|
screen_size: Option<Size2D<Au>>,
|
||||||
|
|
||||||
/// This is used to root reader data.
|
|
||||||
layout_refs: ~[@mut LayoutData],
|
|
||||||
|
|
||||||
display_list: Option<Arc<DisplayList<AbstractNode<()>>>>,
|
display_list: Option<Arc<DisplayList<AbstractNode<()>>>>,
|
||||||
|
|
||||||
css_select_ctx: @mut SelectCtx,
|
css_select_ctx: @mut SelectCtx,
|
||||||
|
@ -123,7 +120,6 @@ impl LayoutTask {
|
||||||
|
|
||||||
display_list: None,
|
display_list: None,
|
||||||
|
|
||||||
layout_refs: ~[],
|
|
||||||
css_select_ctx: @mut new_css_select_ctx(),
|
css_select_ctx: @mut new_css_select_ctx(),
|
||||||
profiler_chan: profiler_chan,
|
profiler_chan: profiler_chan,
|
||||||
}
|
}
|
||||||
|
@ -210,7 +206,7 @@ impl LayoutTask {
|
||||||
//
|
//
|
||||||
// FIXME: This is inefficient. We don't need an entire traversal to do this!
|
// FIXME: This is inefficient. We don't need an entire traversal to do this!
|
||||||
do profile(time::LayoutAuxInitCategory, self.profiler_chan.clone()) {
|
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.
|
// Perform CSS selector matching if necessary.
|
||||||
|
@ -328,7 +324,7 @@ impl LayoutTask {
|
||||||
};
|
};
|
||||||
assert!(node.has_layout_data(), "Node has display item but no layout data");
|
assert!(node.has_layout_data(), "Node has display item but no layout data");
|
||||||
|
|
||||||
let layout_data = node.layout_data();
|
do node.write_layout_data |layout_data| {
|
||||||
layout_data.boxes.display_list = Some(display_list.clone());
|
layout_data.boxes.display_list = Some(display_list.clone());
|
||||||
|
|
||||||
if layout_data.boxes.range.is_none() {
|
if layout_data.boxes.range.is_none() {
|
||||||
|
@ -345,6 +341,7 @@ impl LayoutTask {
|
||||||
layout_data.boxes.range.unwrap().extend_by(1);
|
layout_data.boxes.range.unwrap().extend_by(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let render_layer = RenderLayer {
|
let render_layer = RenderLayer {
|
||||||
display_list: display_list.clone(),
|
display_list: display_list.clone(),
|
||||||
|
@ -376,7 +373,8 @@ impl LayoutTask {
|
||||||
transmute(node)
|
transmute(node)
|
||||||
};
|
};
|
||||||
|
|
||||||
let response = match (node.layout_data().boxes.display_list.clone(), node.layout_data().boxes.range) {
|
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)) => {
|
(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() {
|
||||||
|
@ -398,6 +396,7 @@ impl LayoutTask {
|
||||||
error!("no display list present");
|
error!("no display list present");
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
reply_chan.send(response)
|
reply_chan.send(response)
|
||||||
|
@ -408,7 +407,8 @@ impl LayoutTask {
|
||||||
transmute(node)
|
transmute(node)
|
||||||
};
|
};
|
||||||
|
|
||||||
let response = match (node.layout_data().boxes.display_list.clone(), node.layout_data().boxes.range) {
|
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)) => {
|
(Some(display_list), Some(range)) => {
|
||||||
let mut boxes = ~[];
|
let mut boxes = ~[];
|
||||||
for i in range.eachi() {
|
for i in range.eachi() {
|
||||||
|
@ -418,6 +418,7 @@ impl LayoutTask {
|
||||||
Ok(ContentBoxesResponse(boxes))
|
Ok(ContentBoxesResponse(boxes))
|
||||||
}
|
}
|
||||||
_ => Err(()),
|
_ => Err(()),
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
reply_chan.send(response)
|
reply_chan.send(response)
|
||||||
|
|
|
@ -16,12 +16,17 @@ use dom::htmliframeelement::HTMLIFrameElement;
|
||||||
use dom::text::Text;
|
use dom::text::Text;
|
||||||
|
|
||||||
use std::cast;
|
use std::cast;
|
||||||
|
use std::cell::Cell;
|
||||||
use std::cast::transmute;
|
use std::cast::transmute;
|
||||||
use std::libc::c_void;
|
use std::libc::c_void;
|
||||||
|
use extra::arc::Arc;
|
||||||
use js::jsapi::{JSObject, JSContext};
|
use js::jsapi::{JSObject, JSContext};
|
||||||
use js::rust::Compartment;
|
use js::rust::Compartment;
|
||||||
use netsurfcss::util::VoidPtrLike;
|
use netsurfcss::util::VoidPtrLike;
|
||||||
|
use newcss::complete::CompleteSelectResults;
|
||||||
use servo_util::tree::{TreeNode, TreeNodeRef};
|
use servo_util::tree::{TreeNode, TreeNodeRef};
|
||||||
|
use servo_util::range::Range;
|
||||||
|
use gfx::display_list::DisplayList;
|
||||||
|
|
||||||
//
|
//
|
||||||
// The basic Node structure
|
// The basic Node structure
|
||||||
|
@ -56,7 +61,7 @@ pub struct AbstractNodeChildrenIterator<View> {
|
||||||
///
|
///
|
||||||
/// `View` describes extra data associated with this node that this task has access to. For
|
/// `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
|
/// the script task, this is the unit type `()`. For the layout task, this is
|
||||||
/// `layout::aux::LayoutData`.
|
/// `LayoutData`.
|
||||||
pub struct Node<View> {
|
pub struct Node<View> {
|
||||||
/// The JavaScript wrapper for this node.
|
/// The JavaScript wrapper for this node.
|
||||||
wrapper: WrapperCache,
|
wrapper: WrapperCache,
|
||||||
|
@ -85,7 +90,7 @@ pub struct Node<View> {
|
||||||
owner_doc: Option<AbstractDocument>,
|
owner_doc: Option<AbstractDocument>,
|
||||||
|
|
||||||
/// Layout information. Only the layout task may touch this data.
|
/// Layout information. Only the layout task may touch this data.
|
||||||
priv layout_data: Option<@mut ()>
|
priv layout_data: Option<LayoutData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The different types of nodes.
|
/// The different types of nodes.
|
||||||
|
@ -172,31 +177,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
|
// Convenience accessors
|
||||||
|
|
||||||
/// Returns the type ID of this node. Fails if this node is borrowed mutably.
|
/// Returns the type ID of this node. Fails if this node is borrowed mutably.
|
||||||
|
@ -631,3 +611,65 @@ 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> {
|
||||||
|
pub fn set_layout_data(self, data: LayoutData) {
|
||||||
|
let cell = Cell::new(data);
|
||||||
|
do self.with_mut_base |b| {
|
||||||
|
b.layout_data = Some(cell.take());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_layout_data(self) -> bool {
|
||||||
|
do self.with_base |b| {
|
||||||
|
b.layout_data.is_some()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.get_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_layout_data<R>(self, blk: &fn(data: &mut LayoutData) -> R) -> R {
|
||||||
|
do self.with_mut_base |b| {
|
||||||
|
blk(b.layout_data.get_mut_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue