Skip to content

Commit

Permalink
auto merge of #646 : kmcallister/servo/incremental-layout, r=metajack
Browse files Browse the repository at this point in the history
This is a first attempt at incremental layout.  When recomputing styles, we compare old and new CSS properties to determine which layout steps can be skipped.

Since I'm new to Servo I'm not sure that my code matches the idioms of the project.  Please don't hold back with review comments :)
  • Loading branch information
bors-servo committed Aug 1, 2013
2 parents bb51a9d + b266b5a commit 1d04d5f
Show file tree
Hide file tree
Showing 19 changed files with 385 additions and 79 deletions.
14 changes: 4 additions & 10 deletions src/components/gfx/font_context.rs
Expand Up @@ -134,11 +134,8 @@ impl<'self> FontContext {
let transformed_family_name = self.transform_family(family_name);
debug!("(create font group) transformed family is `%s`", transformed_family_name);

let result = match self.font_list {
Some(ref fl) => {
fl.find_font_in_family(transformed_family_name, style)
},
None => None,
let result = do self.font_list.chain_ref |fl| {
fl.find_font_in_family(transformed_family_name, style)
};

let mut found = false;
Expand All @@ -162,11 +159,8 @@ impl<'self> FontContext {
let last_resort = FontList::get_last_resort_font_families();

for last_resort.iter().advance |family| {
let result = match self.font_list {
Some(ref fl) => {
fl.find_font_in_family(*family, style)
},
None => None,
let result = do self.font_list.chain_ref |fl| {
fl.find_font_in_family(*family, style)
};

for result.iter().advance |font_entry| {
Expand Down
8 changes: 3 additions & 5 deletions src/components/gfx/opts.rs
Expand Up @@ -78,11 +78,9 @@ pub fn from_cmdline_args(args: &[~str]) -> Opts {
None => 1, // FIXME: Number of cores.
};

let profiler_period: Option<float> =
// if only flag is present, default to 5 second period
match getopts::opt_default(&opt_match, "p", "5") {
Some(period) => Some(float::from_str(period).get()),
None => None,
// if only flag is present, default to 5 second period
let profiler_period = do getopts::opt_default(&opt_match, "p", "5").map |period| {
float::from_str(*period).get()
};

let exit_after_load = getopts::opt_present(&opt_match, "x");
Expand Down
8 changes: 8 additions & 0 deletions src/components/main/css/matching.rs
Expand Up @@ -6,6 +6,7 @@

use css::node_util::NodeUtil;
use css::select_handler::NodeSelectHandler;
use layout::incremental;

use script::dom::node::{AbstractNode, LayoutView};
use newcss::complete::CompleteSelectResults;
Expand All @@ -31,6 +32,13 @@ impl MatchMethods for AbstractNode<LayoutView> {
let incomplete_results = select_ctx.select_style(self, &select_handler);
// Combine this node's results with its parent's to resolve all inherited values
let complete_results = compose_results(*self, incomplete_results);

// If there was an existing style, compute the damage that
// incremental layout will need to fix.
if self.have_css_select_results() {
let damage = incremental::compute_damage(self, self.get_css_select_results(), &complete_results);
self.set_restyle_damage(damage);
}
self.set_css_select_results(complete_results);
}

Expand Down
6 changes: 6 additions & 0 deletions src/components/main/css/node_style.rs
Expand Up @@ -5,13 +5,15 @@
// Style retrieval from DOM elements.

use css::node_util::NodeUtil;
use layout::incremental::RestyleDamage;

use newcss::complete::CompleteStyle;
use script::dom::node::{AbstractNode, LayoutView};

/// Node mixin providing `style` method that returns a `NodeStyle`
pub trait StyledNode {
fn style(&self) -> CompleteStyle;
fn restyle_damage(&self) -> RestyleDamage;
}

impl StyledNode for AbstractNode<LayoutView> {
Expand All @@ -20,4 +22,8 @@ impl StyledNode for AbstractNode<LayoutView> {
let results = self.get_css_select_results();
results.computed_style()
}

fn restyle_damage(&self) -> RestyleDamage {
self.get_restyle_damage()
}
}
36 changes: 36 additions & 0 deletions src/components/main/css/node_util.rs
Expand Up @@ -3,6 +3,7 @@
* 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 newcss::complete::CompleteSelectResults;
Expand All @@ -11,6 +12,10 @@ use script::dom::node::{AbstractNode, LayoutView};
pub trait NodeUtil<'self> {
fn get_css_select_results(self) -> &'self CompleteSelectResults;
fn set_css_select_results(self, decl: CompleteSelectResults);
fn have_css_select_results(self) -> bool;

fn get_restyle_damage(self) -> RestyleDamage;
fn set_restyle_damage(self, damage: RestyleDamage);
}

impl<'self> NodeUtil<'self> for AbstractNode<LayoutView> {
Expand All @@ -32,6 +37,11 @@ impl<'self> NodeUtil<'self> for AbstractNode<LayoutView> {
}
}

/// 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()
}

/// 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() {
Expand All @@ -40,4 +50,30 @@ impl<'self> NodeUtil<'self> for AbstractNode<LayoutView> {

self.layout_data().style = Some(decl);
}

/// Get the description of how to account for recent style changes.
/// This is a simple bitfield and fine to copy by value.
fn get_restyle_damage(self) -> RestyleDamage {
// For DOM elements, if we haven't computed damage yet, assume the worst.
// Other nodes don't have styles.
let default = if self.is_element() {
RestyleDamage::all()
} else {
RestyleDamage::none()
};

if !self.has_layout_data() {
return default;
}
self.layout_data().restyle_damage.get_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);
}
}
15 changes: 6 additions & 9 deletions src/components/main/css/select_handler.rs
Expand Up @@ -30,17 +30,14 @@ impl SelectHandler<AbstractNode<LayoutView>> for NodeSelectHandler {

fn named_parent_node(&self, node: &AbstractNode<LayoutView>, name: &str)
-> Option<AbstractNode<LayoutView>> {
match node.parent_node() {
Some(parent) => {
do with_node_name(parent) |node_name| {
if eq_slice(name, node_name) {
Some(parent)
} else {
None
}
do node.parent_node().chain |parent| {
do with_node_name(parent) |node_name| {
if eq_slice(name, node_name) {
Some(parent)
} else {
None
}
}
None => None
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/components/main/layout/aux.rs
Expand Up @@ -5,6 +5,7 @@
//! Code for managing the layout data in the DOM.

use layout::flow::FlowContext;
use layout::incremental::RestyleDamage;

use newcss::complete::CompleteSelectResults;
use script::dom::node::{AbstractNode, LayoutView};
Expand All @@ -15,6 +16,9 @@ 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 CSS flow that this node is associated with.
flow: Option<FlowContext>,
}
Expand All @@ -24,6 +28,7 @@ impl LayoutData {
pub fn new() -> LayoutData {
LayoutData {
style: None,
restyle_damage: None,
flow: None,
}
}
Expand Down
15 changes: 3 additions & 12 deletions src/components/main/layout/box_builder.rs
Expand Up @@ -356,14 +356,8 @@ impl LayoutTreeBuilder {
sibling_generator: Option<@mut BoxGenerator>)
-> Option<(@mut BoxGenerator, @mut BoxGenerator)> {

fn is_root(node: AbstractNode<LayoutView>) -> bool {
match node.parent_node() {
None => true,
Some(_) => false
}
}
let display = if node.is_element() {
match node.style().display(is_root(node)) {
match node.style().display(node.is_root()) {
CSSDisplayNone => return None, // tree ends here if 'display: none'
// TODO(eatkinson) these are hacks so that the code doesn't crash
// when unsupported display values are used. They should be deleted
Expand All @@ -390,11 +384,8 @@ impl LayoutTreeBuilder {
}
};

let sibling_flow: Option<FlowContext> = match sibling_generator {
None => None,
Some(gen) => Some(gen.flow)
};

let sibling_flow: Option<FlowContext> = sibling_generator.map(|gen| gen.flow);

// TODO(eatkinson): use the value of the float property to
// determine whether to float left or right.
let is_float = if (node.is_element()) {
Expand Down
5 changes: 1 addition & 4 deletions src/components/main/layout/float_context.rs
Expand Up @@ -291,10 +291,7 @@ impl FloatContextBase{
}
}

match max_height {
None => None,
Some(h) => Some(h + self.offset.y)
}
max_height.map(|h| h + self.offset.y)
}

/// Given necessary info, finds the closest place a box can be positioned
Expand Down
26 changes: 19 additions & 7 deletions src/components/main/layout/flow.rs
Expand Up @@ -32,6 +32,8 @@ use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
use layout::inline::{InlineFlowData};
use layout::float_context::{FloatContext, Invalid, FloatType};
use layout::incremental::RestyleDamage;
use css::node_style::StyledNode;

use std::cell::Cell;
use std::uint;
Expand All @@ -45,6 +47,7 @@ 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.
#[deriving(Clone)]
pub enum FlowContext {
AbsoluteFlow(@mut FlowData),
BlockFlow(@mut BlockFlowData),
Expand All @@ -64,12 +67,6 @@ pub enum FlowContextType {
Flow_Table
}

impl Clone for FlowContext {
fn clone(&self) -> FlowContext {
*self
}
}

impl FlowContext {
pub fn teardown(&self) {
match *self {
Expand All @@ -84,6 +81,9 @@ impl FlowContext {

/// Like traverse_preorder, but don't end the whole traversal if the callback
/// returns false.
//
// FIXME: Unify this with traverse_preorder_prune, which takes a separate
// 'prune' function.
fn partially_traverse_preorder(&self, callback: &fn(FlowContext) -> bool) {
if !callback((*self).clone()) {
return;
Expand Down Expand Up @@ -157,6 +157,7 @@ impl TreeNodeRef<FlowData> for FlowContext {
/// `CommonFlowInfo`?
pub struct FlowData {
node: AbstractNode<LayoutView>,
restyle_damage: RestyleDamage,

parent: Option<FlowContext>,
first_child: Option<FlowContext>,
Expand Down Expand Up @@ -225,6 +226,7 @@ impl FlowData {
pub fn new(id: int, node: AbstractNode<LayoutView>) -> FlowData {
FlowData {
node: node,
restyle_damage: node.restyle_damage(),

parent: None,
first_child: None,
Expand Down Expand Up @@ -264,6 +266,15 @@ impl<'self> FlowContext {
}
}

/// A convenience method to return the restyle damage of this flow. Fails if the flow is
/// currently being borrowed mutably.
#[inline(always)]
pub fn restyle_damage(&self) -> RestyleDamage {
do self.with_base |info| {
info.restyle_damage
}
}

pub fn inline(&self) -> @mut InlineFlowData {
match *self {
InlineFlow(info) => info,
Expand Down Expand Up @@ -448,7 +459,8 @@ impl<'self> FlowContext {
};

do self.with_base |base| {
fmt!("f%? %? floats %? size %?", base.id, repr, base.num_floats, base.position)
fmt!("f%? %? floats %? size %? damage %?", base.id, repr, base.num_floats,
base.position, base.restyle_damage)
}
}
}
Expand Down

0 comments on commit 1d04d5f

Please sign in to comment.