зеркало из https://github.com/mozilla/gecko-dev.git
servo: Merge #646 - First attempt at incremental layout (from kmcallister:incremental-layout); r=metajack
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 :) Source-Repo: https://github.com/servo/servo Source-Revision: 1d04d5f1bc90d7ddd27718fe7ea2b9866f94d79b
This commit is contained in:
Родитель
4a8d34aaed
Коммит
0b24a946b2
|
@ -134,11 +134,8 @@ impl<'self> FontContext {
|
||||||
let transformed_family_name = self.transform_family(family_name);
|
let transformed_family_name = self.transform_family(family_name);
|
||||||
debug!("(create font group) transformed family is `%s`", transformed_family_name);
|
debug!("(create font group) transformed family is `%s`", transformed_family_name);
|
||||||
|
|
||||||
let result = match self.font_list {
|
let result = do self.font_list.chain_ref |fl| {
|
||||||
Some(ref fl) => {
|
fl.find_font_in_family(transformed_family_name, style)
|
||||||
fl.find_font_in_family(transformed_family_name, style)
|
|
||||||
},
|
|
||||||
None => None,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut found = false;
|
let mut found = false;
|
||||||
|
@ -162,11 +159,8 @@ impl<'self> FontContext {
|
||||||
let last_resort = FontList::get_last_resort_font_families();
|
let last_resort = FontList::get_last_resort_font_families();
|
||||||
|
|
||||||
for last_resort.iter().advance |family| {
|
for last_resort.iter().advance |family| {
|
||||||
let result = match self.font_list {
|
let result = do self.font_list.chain_ref |fl| {
|
||||||
Some(ref fl) => {
|
fl.find_font_in_family(*family, style)
|
||||||
fl.find_font_in_family(*family, style)
|
|
||||||
},
|
|
||||||
None => None,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
for result.iter().advance |font_entry| {
|
for result.iter().advance |font_entry| {
|
||||||
|
|
|
@ -78,11 +78,9 @@ pub fn from_cmdline_args(args: &[~str]) -> Opts {
|
||||||
None => 1, // FIXME: Number of cores.
|
None => 1, // FIXME: Number of cores.
|
||||||
};
|
};
|
||||||
|
|
||||||
let profiler_period: Option<float> =
|
// if only flag is present, default to 5 second period
|
||||||
// if only flag is present, default to 5 second period
|
let profiler_period = do getopts::opt_default(&opt_match, "p", "5").map |period| {
|
||||||
match getopts::opt_default(&opt_match, "p", "5") {
|
float::from_str(*period).get()
|
||||||
Some(period) => Some(float::from_str(period).get()),
|
|
||||||
None => None,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let exit_after_load = getopts::opt_present(&opt_match, "x");
|
let exit_after_load = getopts::opt_present(&opt_match, "x");
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
use css::node_util::NodeUtil;
|
use css::node_util::NodeUtil;
|
||||||
use css::select_handler::NodeSelectHandler;
|
use css::select_handler::NodeSelectHandler;
|
||||||
|
use layout::incremental;
|
||||||
|
|
||||||
use script::dom::node::{AbstractNode, LayoutView};
|
use script::dom::node::{AbstractNode, LayoutView};
|
||||||
use newcss::complete::CompleteSelectResults;
|
use newcss::complete::CompleteSelectResults;
|
||||||
|
@ -31,6 +32,13 @@ impl MatchMethods for AbstractNode<LayoutView> {
|
||||||
let incomplete_results = select_ctx.select_style(self, &select_handler);
|
let incomplete_results = select_ctx.select_style(self, &select_handler);
|
||||||
// Combine this node's results with its parent's to resolve all inherited values
|
// Combine this node's results with its parent's to resolve all inherited values
|
||||||
let complete_results = compose_results(*self, incomplete_results);
|
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);
|
self.set_css_select_results(complete_results);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
// Style retrieval from DOM elements.
|
// Style retrieval from DOM elements.
|
||||||
|
|
||||||
use css::node_util::NodeUtil;
|
use css::node_util::NodeUtil;
|
||||||
|
use layout::incremental::RestyleDamage;
|
||||||
|
|
||||||
use newcss::complete::CompleteStyle;
|
use newcss::complete::CompleteStyle;
|
||||||
use script::dom::node::{AbstractNode, LayoutView};
|
use script::dom::node::{AbstractNode, LayoutView};
|
||||||
|
@ -12,6 +13,7 @@ use script::dom::node::{AbstractNode, LayoutView};
|
||||||
/// Node mixin providing `style` method that returns a `NodeStyle`
|
/// Node mixin providing `style` method that returns a `NodeStyle`
|
||||||
pub trait StyledNode {
|
pub trait StyledNode {
|
||||||
fn style(&self) -> CompleteStyle;
|
fn style(&self) -> CompleteStyle;
|
||||||
|
fn restyle_damage(&self) -> RestyleDamage;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledNode for AbstractNode<LayoutView> {
|
impl StyledNode for AbstractNode<LayoutView> {
|
||||||
|
@ -20,4 +22,8 @@ impl StyledNode for AbstractNode<LayoutView> {
|
||||||
let results = self.get_css_select_results();
|
let results = self.get_css_select_results();
|
||||||
results.computed_style()
|
results.computed_style()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn restyle_damage(&self) -> RestyleDamage {
|
||||||
|
self.get_restyle_damage()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
* 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::aux::LayoutAuxMethods;
|
||||||
|
use layout::incremental::RestyleDamage;
|
||||||
|
|
||||||
use std::cast::transmute;
|
use std::cast::transmute;
|
||||||
use newcss::complete::CompleteSelectResults;
|
use newcss::complete::CompleteSelectResults;
|
||||||
|
@ -11,6 +12,10 @@ use script::dom::node::{AbstractNode, LayoutView};
|
||||||
pub trait NodeUtil<'self> {
|
pub trait NodeUtil<'self> {
|
||||||
fn get_css_select_results(self) -> &'self CompleteSelectResults;
|
fn get_css_select_results(self) -> &'self CompleteSelectResults;
|
||||||
fn set_css_select_results(self, decl: 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> {
|
impl<'self> NodeUtil<'self> for AbstractNode<LayoutView> {
|
||||||
|
@ -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.
|
/// Update the computed style of an HTML element with a style specified by CSS.
|
||||||
fn set_css_select_results(self, decl: CompleteSelectResults) {
|
fn set_css_select_results(self, decl: CompleteSelectResults) {
|
||||||
if !self.has_layout_data() {
|
if !self.has_layout_data() {
|
||||||
|
@ -40,4 +50,30 @@ impl<'self> NodeUtil<'self> for AbstractNode<LayoutView> {
|
||||||
|
|
||||||
self.layout_data().style = Some(decl);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,17 +30,14 @@ impl SelectHandler<AbstractNode<LayoutView>> for NodeSelectHandler {
|
||||||
|
|
||||||
fn named_parent_node(&self, node: &AbstractNode<LayoutView>, name: &str)
|
fn named_parent_node(&self, node: &AbstractNode<LayoutView>, name: &str)
|
||||||
-> Option<AbstractNode<LayoutView>> {
|
-> Option<AbstractNode<LayoutView>> {
|
||||||
match node.parent_node() {
|
do node.parent_node().chain |parent| {
|
||||||
Some(parent) => {
|
do with_node_name(parent) |node_name| {
|
||||||
do with_node_name(parent) |node_name| {
|
if eq_slice(name, node_name) {
|
||||||
if eq_slice(name, node_name) {
|
Some(parent)
|
||||||
Some(parent)
|
} else {
|
||||||
} else {
|
None
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
//! Code for managing the layout data in the DOM.
|
//! Code for managing the layout data in the DOM.
|
||||||
|
|
||||||
use layout::flow::FlowContext;
|
use layout::flow::FlowContext;
|
||||||
|
use layout::incremental::RestyleDamage;
|
||||||
|
|
||||||
use newcss::complete::CompleteSelectResults;
|
use newcss::complete::CompleteSelectResults;
|
||||||
use script::dom::node::{AbstractNode, LayoutView};
|
use script::dom::node::{AbstractNode, LayoutView};
|
||||||
|
@ -15,6 +16,9 @@ pub struct LayoutData {
|
||||||
/// The results of CSS styling for this node.
|
/// The results of CSS styling for this node.
|
||||||
style: Option<CompleteSelectResults>,
|
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.
|
/// The CSS flow that this node is associated with.
|
||||||
flow: Option<FlowContext>,
|
flow: Option<FlowContext>,
|
||||||
}
|
}
|
||||||
|
@ -24,6 +28,7 @@ impl LayoutData {
|
||||||
pub fn new() -> LayoutData {
|
pub fn new() -> LayoutData {
|
||||||
LayoutData {
|
LayoutData {
|
||||||
style: None,
|
style: None,
|
||||||
|
restyle_damage: None,
|
||||||
flow: None,
|
flow: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -356,14 +356,8 @@ impl LayoutTreeBuilder {
|
||||||
sibling_generator: Option<@mut BoxGenerator>)
|
sibling_generator: Option<@mut BoxGenerator>)
|
||||||
-> Option<(@mut BoxGenerator, @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() {
|
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'
|
CSSDisplayNone => return None, // tree ends here if 'display: none'
|
||||||
// TODO(eatkinson) these are hacks so that the code doesn't crash
|
// TODO(eatkinson) these are hacks so that the code doesn't crash
|
||||||
// when unsupported display values are used. They should be deleted
|
// when unsupported display values are used. They should be deleted
|
||||||
|
@ -390,11 +384,8 @@ impl LayoutTreeBuilder {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let sibling_flow: Option<FlowContext> = match sibling_generator {
|
let sibling_flow: Option<FlowContext> = sibling_generator.map(|gen| gen.flow);
|
||||||
None => None,
|
|
||||||
Some(gen) => Some(gen.flow)
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO(eatkinson): use the value of the float property to
|
// TODO(eatkinson): use the value of the float property to
|
||||||
// determine whether to float left or right.
|
// determine whether to float left or right.
|
||||||
let is_float = if (node.is_element()) {
|
let is_float = if (node.is_element()) {
|
||||||
|
|
|
@ -291,10 +291,7 @@ impl FloatContextBase{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match max_height {
|
max_height.map(|h| h + self.offset.y)
|
||||||
None => None,
|
|
||||||
Some(h) => Some(h + self.offset.y)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given necessary info, finds the closest place a box can be positioned
|
/// Given necessary info, finds the closest place a box can be positioned
|
||||||
|
|
|
@ -32,6 +32,8 @@ use layout::context::LayoutContext;
|
||||||
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
|
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
|
||||||
use layout::inline::{InlineFlowData};
|
use layout::inline::{InlineFlowData};
|
||||||
use layout::float_context::{FloatContext, Invalid, FloatType};
|
use layout::float_context::{FloatContext, Invalid, FloatType};
|
||||||
|
use layout::incremental::RestyleDamage;
|
||||||
|
use css::node_style::StyledNode;
|
||||||
|
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::uint;
|
use std::uint;
|
||||||
|
@ -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
|
/// The type of the formatting context and data specific to each context, such as line box
|
||||||
/// structures or float lists.
|
/// structures or float lists.
|
||||||
|
#[deriving(Clone)]
|
||||||
pub enum FlowContext {
|
pub enum FlowContext {
|
||||||
AbsoluteFlow(@mut FlowData),
|
AbsoluteFlow(@mut FlowData),
|
||||||
BlockFlow(@mut BlockFlowData),
|
BlockFlow(@mut BlockFlowData),
|
||||||
|
@ -64,12 +67,6 @@ pub enum FlowContextType {
|
||||||
Flow_Table
|
Flow_Table
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for FlowContext {
|
|
||||||
fn clone(&self) -> FlowContext {
|
|
||||||
*self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FlowContext {
|
impl FlowContext {
|
||||||
pub fn teardown(&self) {
|
pub fn teardown(&self) {
|
||||||
match *self {
|
match *self {
|
||||||
|
@ -84,6 +81,9 @@ impl FlowContext {
|
||||||
|
|
||||||
/// Like traverse_preorder, but don't end the whole traversal if the callback
|
/// Like traverse_preorder, but don't end the whole traversal if the callback
|
||||||
/// returns false.
|
/// returns false.
|
||||||
|
//
|
||||||
|
// FIXME: Unify this with traverse_preorder_prune, which takes a separate
|
||||||
|
// 'prune' function.
|
||||||
fn partially_traverse_preorder(&self, callback: &fn(FlowContext) -> bool) {
|
fn partially_traverse_preorder(&self, callback: &fn(FlowContext) -> bool) {
|
||||||
if !callback((*self).clone()) {
|
if !callback((*self).clone()) {
|
||||||
return;
|
return;
|
||||||
|
@ -157,6 +157,7 @@ impl TreeNodeRef<FlowData> for FlowContext {
|
||||||
/// `CommonFlowInfo`?
|
/// `CommonFlowInfo`?
|
||||||
pub struct FlowData {
|
pub struct FlowData {
|
||||||
node: AbstractNode<LayoutView>,
|
node: AbstractNode<LayoutView>,
|
||||||
|
restyle_damage: RestyleDamage,
|
||||||
|
|
||||||
parent: Option<FlowContext>,
|
parent: Option<FlowContext>,
|
||||||
first_child: Option<FlowContext>,
|
first_child: Option<FlowContext>,
|
||||||
|
@ -225,6 +226,7 @@ impl FlowData {
|
||||||
pub fn new(id: int, node: AbstractNode<LayoutView>) -> FlowData {
|
pub fn new(id: int, node: AbstractNode<LayoutView>) -> FlowData {
|
||||||
FlowData {
|
FlowData {
|
||||||
node: node,
|
node: node,
|
||||||
|
restyle_damage: node.restyle_damage(),
|
||||||
|
|
||||||
parent: None,
|
parent: None,
|
||||||
first_child: None,
|
first_child: None,
|
||||||
|
@ -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 {
|
pub fn inline(&self) -> @mut InlineFlowData {
|
||||||
match *self {
|
match *self {
|
||||||
InlineFlow(info) => info,
|
InlineFlow(info) => info,
|
||||||
|
@ -448,7 +459,8 @@ impl<'self> FlowContext {
|
||||||
};
|
};
|
||||||
|
|
||||||
do self.with_base |base| {
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,198 @@
|
||||||
|
/* 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 newcss::complete::CompleteSelectResults;
|
||||||
|
|
||||||
|
use script::dom::node::{AbstractNode, LayoutView};
|
||||||
|
|
||||||
|
/// Individual layout actions that may be necessary after restyling.
|
||||||
|
///
|
||||||
|
/// If you add to this enum, also add the value to RestyleDamage::all below.
|
||||||
|
/// (FIXME: do this automatically)
|
||||||
|
pub enum RestyleEffect {
|
||||||
|
/// Repaint the node itself.
|
||||||
|
/// Currently unused; need to decide how this propagates.
|
||||||
|
Repaint = 0x01,
|
||||||
|
|
||||||
|
/// Recompute intrinsic widths (minimum and preferred).
|
||||||
|
/// Propagates down the flow tree because the computation is
|
||||||
|
/// bottom-up.
|
||||||
|
BubbleWidths = 0x02,
|
||||||
|
|
||||||
|
/// Recompute actual widths and heights.
|
||||||
|
/// Propagates up the flow tree because the computation is
|
||||||
|
/// top-down.
|
||||||
|
Reflow = 0x04,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A set of RestyleEffects.
|
||||||
|
// FIXME: Switch to librustc/util/enum_set.rs if that gets moved into
|
||||||
|
// libextra (Rust #8054)
|
||||||
|
pub struct RestyleDamage {
|
||||||
|
priv bits: int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provide literal syntax of the form restyle_damage!(Repaint, Reflow)
|
||||||
|
macro_rules! restyle_damage(
|
||||||
|
( $($damage:ident),* ) => (
|
||||||
|
RestyleDamage::none() $( .add($damage) )*
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
impl RestyleDamage {
|
||||||
|
pub fn none() -> RestyleDamage {
|
||||||
|
RestyleDamage { bits: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all() -> RestyleDamage {
|
||||||
|
restyle_damage!(Repaint, BubbleWidths, Reflow)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Effects of resizing the window.
|
||||||
|
pub fn for_resize() -> RestyleDamage {
|
||||||
|
RestyleDamage::all()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(self) -> bool {
|
||||||
|
self.bits == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_nonempty(self) -> bool {
|
||||||
|
self.bits != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(self, effect: RestyleEffect) -> RestyleDamage {
|
||||||
|
RestyleDamage { bits: self.bits | (effect as int) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has(self, effect: RestyleEffect) -> bool {
|
||||||
|
(self.bits & (effect as int)) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lacks(self, effect: RestyleEffect) -> bool {
|
||||||
|
(self.bits & (effect as int)) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn union(self, other: RestyleDamage) -> RestyleDamage {
|
||||||
|
RestyleDamage { bits: self.bits | other.bits }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn union_in_place(&mut self, other: RestyleDamage) {
|
||||||
|
self.bits = self.bits | other.bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn intersect(self, other: RestyleDamage) -> RestyleDamage {
|
||||||
|
RestyleDamage { bits: self.bits & other.bits }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Elements of self which should also get set on any ancestor flow.
|
||||||
|
pub fn propagate_up(self) -> RestyleDamage {
|
||||||
|
self.intersect(restyle_damage!(Reflow))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Elements of self which should also get set on any child flows.
|
||||||
|
pub fn propagate_down(self) -> RestyleDamage {
|
||||||
|
self.intersect(restyle_damage!(BubbleWidths))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NB: We need the braces inside the RHS due to Rust #8012. This particular
|
||||||
|
// version of this macro might be safe anyway, but we want to avoid silent
|
||||||
|
// breakage on modifications.
|
||||||
|
macro_rules! add_if_not_equal(
|
||||||
|
([ $($effect:ident),* ], [ $($getter:ident),* ]) => ({
|
||||||
|
if $( (old.$getter() != new.$getter()) )||* {
|
||||||
|
damage.union_in_place( restyle_damage!( $($effect),* ) );
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
pub fn compute_damage(node: &AbstractNode<LayoutView>,
|
||||||
|
old_results: &CompleteSelectResults, new_results: &CompleteSelectResults)
|
||||||
|
-> RestyleDamage {
|
||||||
|
let old = old_results.computed_style();
|
||||||
|
let new = new_results.computed_style();
|
||||||
|
let mut damage = RestyleDamage::none();
|
||||||
|
|
||||||
|
// This checks every CSS property, as enumerated in
|
||||||
|
// impl<'self> CssComputedStyle<'self>
|
||||||
|
// in src/support/netsurfcss/rust-netsurfcss/netsurfcss.rc.
|
||||||
|
|
||||||
|
// FIXME: We can short-circuit more of this.
|
||||||
|
|
||||||
|
add_if_not_equal!([ Repaint ],
|
||||||
|
[ color, background_color, border_top_color, border_right_color,
|
||||||
|
border_bottom_color, border_left_color ]);
|
||||||
|
|
||||||
|
add_if_not_equal!([ Repaint, BubbleWidths, Reflow ],
|
||||||
|
[ border_top_width, border_right_width, border_bottom_width,
|
||||||
|
border_left_width, margin_top, margin_right, margin_bottom, margin_left,
|
||||||
|
padding_top, padding_right, padding_bottom, padding_left, position,
|
||||||
|
width, height, float, font_family, font_size, font_style, font_weight,
|
||||||
|
text_align, text_decoration, line_height ]);
|
||||||
|
|
||||||
|
// Handle 'display' specially because it has this 'is_root' parameter.
|
||||||
|
let is_root = node.is_root();
|
||||||
|
if old.display(is_root) != new.display(is_root) {
|
||||||
|
damage.union_in_place(restyle_damage!(Repaint, BubbleWidths, Reflow));
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: test somehow that we checked every CSS property
|
||||||
|
|
||||||
|
damage
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod restyle_damage_tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn none_is_empty() {
|
||||||
|
let d = RestyleDamage::none();
|
||||||
|
assert!(!d.has(Repaint));
|
||||||
|
assert!(!d.has(BubbleWidths));
|
||||||
|
assert!(d.lacks(Repaint));
|
||||||
|
assert!(d.lacks(BubbleWidths));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn all_is_full() {
|
||||||
|
let d = RestyleDamage::all();
|
||||||
|
assert!(d.has(Repaint));
|
||||||
|
assert!(d.has(BubbleWidths));
|
||||||
|
assert!(!d.lacks(Repaint));
|
||||||
|
assert!(!d.lacks(BubbleWidths));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_add() {
|
||||||
|
assert!(RestyleDamage::none().add(BubbleWidths).has(BubbleWidths));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_union() {
|
||||||
|
let d = restyle_damage!(Repaint).union(restyle_damage!(BubbleWidths));
|
||||||
|
assert!(d.has(Repaint));
|
||||||
|
assert!(d.has(BubbleWidths));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_union_in_place() {
|
||||||
|
let mut d = restyle_damage!(Repaint);
|
||||||
|
d.union_in_place(restyle_damage!(BubbleWidths));
|
||||||
|
assert!(d.has(Repaint));
|
||||||
|
assert!(d.has(BubbleWidths));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_intersect() {
|
||||||
|
let x = restyle_damage!(Repaint, BubbleWidths);
|
||||||
|
let y = restyle_damage!(Repaint, Reflow);
|
||||||
|
let d = x.intersect(y);
|
||||||
|
assert!(d.has(Repaint));
|
||||||
|
assert!(d.lacks(BubbleWidths));
|
||||||
|
assert!(d.lacks(Reflow));
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ 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};
|
||||||
use layout::flow::FlowContext;
|
use layout::flow::FlowContext;
|
||||||
|
use layout::incremental::{RestyleDamage, BubbleWidths};
|
||||||
|
|
||||||
use std::cast::transmute;
|
use std::cast::transmute;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
|
@ -193,6 +194,8 @@ impl LayoutTask {
|
||||||
self.doc_url = Some(doc_url);
|
self.doc_url = Some(doc_url);
|
||||||
let screen_size = Size2D(Au::from_px(data.window_size.width as int),
|
let screen_size = Size2D(Au::from_px(data.window_size.width as int),
|
||||||
Au::from_px(data.window_size.height as int));
|
Au::from_px(data.window_size.height as int));
|
||||||
|
let resized = self.screen_size != Some(screen_size);
|
||||||
|
debug!("resized: %?", resized);
|
||||||
self.screen_size = Some(screen_size);
|
self.screen_size = Some(screen_size);
|
||||||
|
|
||||||
// Create a layout context for use throughout the following passes.
|
// Create a layout context for use throughout the following passes.
|
||||||
|
@ -227,20 +230,59 @@ impl LayoutTask {
|
||||||
layout_root
|
layout_root
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Propagate restyle damage up and down the tree, as appropriate.
|
||||||
|
// FIXME: Merge this with flow tree building and/or the other traversals.
|
||||||
|
for layout_root.traverse_preorder |flow| {
|
||||||
|
// Also set any damage implied by resize.
|
||||||
|
if resized {
|
||||||
|
do flow.with_mut_base |base| {
|
||||||
|
base.restyle_damage.union_in_place(RestyleDamage::for_resize());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let prop = flow.with_base(|base| base.restyle_damage.propagate_down());
|
||||||
|
if prop.is_nonempty() {
|
||||||
|
for flow.each_child |kid_ctx| {
|
||||||
|
do kid_ctx.with_mut_base |kid| {
|
||||||
|
kid.restyle_damage.union_in_place(prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for layout_root.traverse_postorder |flow| {
|
||||||
|
do flow.with_base |base| {
|
||||||
|
match base.parent {
|
||||||
|
None => {},
|
||||||
|
Some(parent_ctx) => {
|
||||||
|
let prop = base.restyle_damage.propagate_up();
|
||||||
|
do parent_ctx.with_mut_base |parent| {
|
||||||
|
parent.restyle_damage.union_in_place(prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
debug!("layout: constructed Flow tree");
|
debug!("layout: constructed Flow tree");
|
||||||
debug!("", layout_root.dump());
|
debug!("%?", layout_root.dump());
|
||||||
|
|
||||||
// Perform the primary layout passes over the flow tree to compute the locations of all
|
// Perform the primary layout passes over the flow tree to compute the locations of all
|
||||||
// the boxes.
|
// the boxes.
|
||||||
do profile(time::LayoutMainCategory, self.profiler_chan.clone()) {
|
do profile(time::LayoutMainCategory, self.profiler_chan.clone()) {
|
||||||
for layout_root.traverse_postorder |flow| {
|
for layout_root.traverse_postorder_prune(|f| f.restyle_damage().lacks(BubbleWidths)) |flow| {
|
||||||
flow.bubble_widths(&mut layout_ctx);
|
flow.bubble_widths(&mut layout_ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// FIXME: We want to do
|
||||||
|
// for layout_root.traverse_preorder_prune(|f| f.restyle_damage().lacks(Reflow)) |flow| {
|
||||||
|
// but FloatContext values can't be reused, so we need to recompute them every time.
|
||||||
for layout_root.traverse_preorder |flow| {
|
for layout_root.traverse_preorder |flow| {
|
||||||
flow.assign_widths(&mut layout_ctx);
|
flow.assign_widths(&mut layout_ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
// For now, this is an inorder traversal
|
// For now, this is an inorder traversal
|
||||||
|
// FIXME: prune this traversal as well
|
||||||
layout_root.assign_height(&mut layout_ctx);
|
layout_root.assign_height(&mut layout_ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,8 +314,6 @@ impl LayoutTask {
|
||||||
} // time(layout: display list building)
|
} // time(layout: display list building)
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("%?", layout_root.dump());
|
|
||||||
|
|
||||||
// Tell script that we're done.
|
// Tell script that we're done.
|
||||||
//
|
//
|
||||||
// FIXME(pcwalton): This should probably be *one* channel, but we can't fix this without
|
// FIXME(pcwalton): This should probably be *one* channel, but we can't fix this without
|
||||||
|
|
|
@ -82,6 +82,7 @@ pub mod layout {
|
||||||
pub mod model;
|
pub mod model;
|
||||||
pub mod text;
|
pub mod text;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
pub mod incremental;
|
||||||
mod aux;
|
mod aux;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,14 +57,11 @@ impl ImageHolder {
|
||||||
/// Query and update the current image size.
|
/// Query and update the current image size.
|
||||||
pub fn get_size(&mut self) -> Option<Size2D<int>> {
|
pub fn get_size(&mut self) -> Option<Size2D<int>> {
|
||||||
debug!("get_size() %?", self.url);
|
debug!("get_size() %?", self.url);
|
||||||
match self.get_image() {
|
do self.get_image().map |img| {
|
||||||
Some(img) => {
|
let img_ref = img.get();
|
||||||
let img_ref = img.get();
|
self.cached_size = Size2D(img_ref.width as int,
|
||||||
self.cached_size = Size2D(img_ref.width as int,
|
img_ref.height as int);
|
||||||
img_ref.height as int);
|
self.cached_size.clone()
|
||||||
Some(copy self.cached_size)
|
|
||||||
},
|
|
||||||
None => None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,12 +86,7 @@ impl ImageHolder {
|
||||||
|
|
||||||
// Clone isn't pure so we have to swap out the mutable image option
|
// Clone isn't pure so we have to swap out the mutable image option
|
||||||
let image = replace(&mut self.image, None);
|
let image = replace(&mut self.image, None);
|
||||||
|
let result = image.clone();
|
||||||
let result = match image {
|
|
||||||
Some(ref image) => Some(image.clone()),
|
|
||||||
None => None
|
|
||||||
};
|
|
||||||
|
|
||||||
replace(&mut self.image, image);
|
replace(&mut self.image, image);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -264,6 +264,11 @@ impl<'self, View> AbstractNode<View> {
|
||||||
self.with_base(|b| b.next_sibling)
|
self.with_base(|b| b.next_sibling)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Is this node a root?
|
||||||
|
pub fn is_root(self) -> bool {
|
||||||
|
self.parent_node().is_none()
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Downcasting borrows
|
// Downcasting borrows
|
||||||
//
|
//
|
||||||
|
@ -415,10 +420,7 @@ impl<'self, View> AbstractNode<View> {
|
||||||
impl<View> Iterator<AbstractNode<View>> for AbstractNodeChildrenIterator<View> {
|
impl<View> Iterator<AbstractNode<View>> for AbstractNodeChildrenIterator<View> {
|
||||||
pub fn next(&mut self) -> Option<AbstractNode<View>> {
|
pub fn next(&mut self) -> Option<AbstractNode<View>> {
|
||||||
let node = self.current_node;
|
let node = self.current_node;
|
||||||
self.current_node = match self.current_node {
|
self.current_node = self.current_node.chain(|node| node.next_sibling());
|
||||||
None => None,
|
|
||||||
Some(node) => node.next_sibling(),
|
|
||||||
};
|
|
||||||
node
|
node
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,20 @@ pub trait TreeUtils {
|
||||||
|
|
||||||
/// Iterates over this node and all its descendants, in postorder.
|
/// Iterates over this node and all its descendants, in postorder.
|
||||||
fn traverse_postorder(&self, callback: &fn(Self) -> bool) -> bool;
|
fn traverse_postorder(&self, callback: &fn(Self) -> bool) -> bool;
|
||||||
|
|
||||||
|
/// Like traverse_preorder but calls 'prune' first on each node. If it returns true then we
|
||||||
|
/// skip the whole subtree but continue iterating.
|
||||||
|
///
|
||||||
|
/// 'prune' is a separate function a) for compatibility with the 'for' protocol,
|
||||||
|
/// b) so that the postorder version can still prune before traversing.
|
||||||
|
fn traverse_preorder_prune(&self, prune: &fn(&Self) -> bool, callback: &fn(Self) -> bool) -> bool;
|
||||||
|
|
||||||
|
/// Like traverse_postorder but calls 'prune' first on each node. If it returns true then we
|
||||||
|
/// skip the whole subtree but continue iterating.
|
||||||
|
///
|
||||||
|
/// NB: 'prune' is called *before* traversing children, even though this is a
|
||||||
|
/// postorder traversal.
|
||||||
|
fn traverse_postorder_prune(&self, prune: &fn(&Self) -> bool, callback: &fn(Self) -> bool) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<NR:TreeNodeRef<N>,N:TreeNode<NR>> TreeUtils for NR {
|
impl<NR:TreeNodeRef<N>,N:TreeNode<NR>> TreeUtils for NR {
|
||||||
|
@ -146,14 +160,19 @@ impl<NR:TreeNodeRef<N>,N:TreeNode<NR>> TreeUtils for NR {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn traverse_preorder(&self, callback: &fn(NR) -> bool) -> bool {
|
fn traverse_preorder_prune(&self, prune: &fn(&NR) -> bool, callback: &fn(NR) -> bool) -> bool {
|
||||||
|
// prune shouldn't mutate, so don't clone
|
||||||
|
if prune(self) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if !callback((*self).clone()) {
|
if !callback((*self).clone()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for self.each_child |kid| {
|
for self.each_child |kid| {
|
||||||
// FIXME: Work around rust#2202. We should be able to pass the callback directly.
|
// FIXME: Work around rust#2202. We should be able to pass the callback directly.
|
||||||
if !kid.traverse_preorder(|a| callback(a)) {
|
if !kid.traverse_preorder_prune(|a| prune(a), |a| callback(a)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -161,15 +180,28 @@ impl<NR:TreeNodeRef<N>,N:TreeNode<NR>> TreeUtils for NR {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn traverse_postorder(&self, callback: &fn(NR) -> bool) -> bool {
|
fn traverse_postorder_prune(&self, prune: &fn(&NR) -> bool, callback: &fn(NR) -> bool) -> bool {
|
||||||
|
// prune shouldn't mutate, so don't clone
|
||||||
|
if prune(self) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
for self.each_child |kid| {
|
for self.each_child |kid| {
|
||||||
// FIXME: Work around rust#2202. We should be able to pass the callback directly.
|
// FIXME: Work around rust#2202. We should be able to pass the callback directly.
|
||||||
if !kid.traverse_postorder(|a| callback(a)) {
|
if !kid.traverse_postorder_prune(|a| prune(a), |a| callback(a)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
callback((*self).clone())
|
callback((*self).clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn traverse_preorder(&self, callback: &fn(NR) -> bool) -> bool {
|
||||||
|
self.traverse_preorder_prune(|_| false, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn traverse_postorder(&self, callback: &fn(NR) -> bool) -> bool {
|
||||||
|
self.traverse_postorder_prune(|_| false, callback)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,10 +11,7 @@ pub trait BinarySearchMethods<'self, T: Ord + Eq> {
|
||||||
|
|
||||||
impl<'self, T: Ord + Eq> BinarySearchMethods<'self, T> for &'self [T] {
|
impl<'self, T: Ord + Eq> BinarySearchMethods<'self, T> for &'self [T] {
|
||||||
fn binary_search(&self, key: &T) -> Option<&'self T> {
|
fn binary_search(&self, key: &T) -> Option<&'self T> {
|
||||||
match self.binary_search_index(key) {
|
self.binary_search_index(key).map(|i| &self[*i])
|
||||||
None => None,
|
|
||||||
Some(i) => Some(&self[i])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn binary_search_index(&self, key: &T) -> Option<uint> {
|
fn binary_search_index(&self, key: &T) -> Option<uint> {
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
window.setTimeout(function () {
|
window.setTimeout(function () {
|
||||||
document.getElementsByTagName('div')[0].setAttribute('class', 'blue');
|
window.document.getElementsByTagName('div')[0].setAttribute('class', 'blue');
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче