diff options
-rw-r--r-- | components/layout/construct.rs | 88 | ||||
-rw-r--r-- | components/layout/css/node_style.rs | 5 | ||||
-rw-r--r-- | components/layout/css/node_util.rs | 15 | ||||
-rw-r--r-- | components/layout/flow.rs | 98 | ||||
-rw-r--r-- | components/layout/layout_task.rs | 33 | ||||
-rw-r--r-- | components/layout/traversal.rs | 33 | ||||
-rw-r--r-- | components/layout/wrapper.rs | 27 | ||||
-rw-r--r-- | components/script/dom/node.rs | 68 | ||||
-rw-r--r-- | components/style/node.rs | 6 | ||||
-rw-r--r-- | components/util/opts.rs | 6 | ||||
-rw-r--r-- | components/util/time.rs | 6 | ||||
-rw-r--r-- | ports/cef/core.rs | 1 | ||||
-rw-r--r-- | tests/reftest.rs | 2 |
13 files changed, 308 insertions, 80 deletions
diff --git a/components/layout/construct.rs b/components/layout/construct.rs index 7d4b6d24b81..58747ca13e9 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -43,7 +43,7 @@ use table_rowgroup::TableRowGroupFlow; use table_row::TableRowFlow; use table_cell::TableCellFlow; use text::TextRunScanner; -use util::{LayoutDataAccess, OpaqueNodeMethods}; +use util::{LayoutDataAccess, OpaqueNodeMethods, LayoutDataWrapper}; use wrapper::{PostorderNodeMutTraversal, TLayoutNode, ThreadSafeLayoutNode}; use wrapper::{Before, After, Normal}; @@ -80,6 +80,22 @@ pub enum ConstructionResult { ConstructionItemConstructionResult(ConstructionItem), } +impl ConstructionResult { + pub fn swap_out(&mut self, layout_context: &LayoutContext) -> ConstructionResult { + if layout_context.shared.opts.incremental_layout { + match *self { + NoConstructionResult => + return NoConstructionResult, + FlowConstructionResult(ref flow_ref, ref abs_descendants) => + return FlowConstructionResult((*flow_ref).clone(), (*abs_descendants).clone()), + ConstructionItemConstructionResult(_) => {}, + } + } + + mem::replace(self, NoConstructionResult) + } +} + /// Represents the output of flow construction for a DOM node that has not yet resulted in a /// complete flow. Construction items bubble up the tree until they find a `Flow` to be attached /// to. @@ -345,7 +361,7 @@ impl<'a> FlowConstructor<'a> { &mut InlineFragmentsAccumulator, abs_descendants: &mut Descendants, first_fragment: &mut bool) { - match kid.swap_out_construction_result() { + match kid.swap_out_construction_result(self.layout_context) { NoConstructionResult => {} FlowConstructionResult(kid_flow, kid_abs_descendants) => { // If kid_flow is TableCaptionFlow, kid_flow should be added under @@ -546,7 +562,7 @@ impl<'a> FlowConstructor<'a> { if kid.get_pseudo_element_type() != Normal { self.process(&kid); } - match kid.swap_out_construction_result() { + match kid.swap_out_construction_result(self.layout_context) { NoConstructionResult => {} FlowConstructionResult(flow, kid_abs_descendants) => { // {ib} split. Flush the accumulator to our new split and make a new @@ -727,12 +743,13 @@ impl<'a> FlowConstructor<'a> { table_wrapper_flow: &mut FlowRef, node: &ThreadSafeLayoutNode) { for kid in node.children() { - match kid.swap_out_construction_result() { + match kid.swap_out_construction_result(self.layout_context) { NoConstructionResult | ConstructionItemConstructionResult(_) => {} FlowConstructionResult(kid_flow, _) => { // Only kid flows with table-caption are matched here. - assert!(kid_flow.get().is_table_caption()); - table_wrapper_flow.add_new_child(kid_flow); + if kid_flow.get().is_table_caption() { + table_wrapper_flow.add_new_child(kid_flow); + } } } } @@ -889,7 +906,7 @@ impl<'a> FlowConstructor<'a> { for kid in node.children() { // CSS 2.1 § 17.2.1. Treat all non-column child fragments of `table-column-group` // as `display: none`. - match kid.swap_out_construction_result() { + match kid.swap_out_construction_result(self.layout_context) { ConstructionItemConstructionResult(TableColumnFragmentConstructionItem( fragment)) => { col_fragments.push(fragment); @@ -958,7 +975,7 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> { // results of children. (display::none, _, _) => { for child in node.children() { - drop(child.swap_out_construction_result()) + drop(child.swap_out_construction_result(self.layout_context)) } } @@ -1063,12 +1080,14 @@ trait NodeUtils { /// Returns true if this node doesn't render its kids and false otherwise. fn is_replaced_content(&self) -> bool; + fn get_construction_result<'a>(self, layout_data: &'a mut LayoutDataWrapper) -> &'a mut ConstructionResult; + /// Sets the construction result of a flow. - fn set_flow_construction_result(&self, result: ConstructionResult); + fn set_flow_construction_result(self, result: ConstructionResult); /// Replaces the flow construction result in a node with `NoConstructionResult` and returns the /// old value. - fn swap_out_construction_result(&self) -> ConstructionResult; + fn swap_out_construction_result(self, layout_context: &LayoutContext) -> ConstructionResult; } impl<'ln> NodeUtils for ThreadSafeLayoutNode<'ln> { @@ -1087,43 +1106,30 @@ impl<'ln> NodeUtils for ThreadSafeLayoutNode<'ln> { } } + fn get_construction_result<'a>(self, layout_data: &'a mut LayoutDataWrapper) -> &'a mut ConstructionResult { + match self.get_pseudo_element_type() { + Before(_) => &mut layout_data.data.before_flow_construction_result, + After (_) => &mut layout_data.data.after_flow_construction_result, + Normal => &mut layout_data.data.flow_construction_result, + } + } + #[inline(always)] - fn set_flow_construction_result(&self, result: ConstructionResult) { + fn set_flow_construction_result(self, result: ConstructionResult) { let mut layout_data_ref = self.mutate_layout_data(); - match &mut *layout_data_ref { - &Some(ref mut layout_data) =>{ - match self.get_pseudo_element_type() { - Before(_) => layout_data.data.before_flow_construction_result = result, - After(_) => layout_data.data.after_flow_construction_result = result, - Normal => layout_data.data.flow_construction_result = result, - } - }, - &None => fail!("no layout data"), - } + let layout_data = layout_data_ref.as_mut().expect("no layout data"); + + let dst = self.get_construction_result(layout_data); + + *dst = result; } #[inline(always)] - fn swap_out_construction_result(&self) -> ConstructionResult { + fn swap_out_construction_result(self, layout_context: &LayoutContext) -> ConstructionResult { let mut layout_data_ref = self.mutate_layout_data(); - match &mut *layout_data_ref { - &Some(ref mut layout_data) => { - match self.get_pseudo_element_type() { - Before(_) => { - mem::replace(&mut layout_data.data.before_flow_construction_result, - NoConstructionResult) - } - After(_) => { - mem::replace(&mut layout_data.data.after_flow_construction_result, - NoConstructionResult) - } - Normal => { - mem::replace(&mut layout_data.data.flow_construction_result, - NoConstructionResult) - } - } - } - &None => fail!("no layout data"), - } + let layout_data = layout_data_ref.as_mut().expect("no layout data"); + + self.get_construction_result(layout_data).swap_out(layout_context) } } diff --git a/components/layout/css/node_style.rs b/components/layout/css/node_style.rs index 8512429442a..35bb1b1a8fa 100644 --- a/components/layout/css/node_style.rs +++ b/components/layout/css/node_style.rs @@ -14,6 +14,7 @@ use sync::Arc; /// Node mixin providing `style` method that returns a `NodeStyle` pub trait StyledNode { fn style<'a>(&'a self) -> &'a Arc<ComputedValues>; + fn unstyle(self); fn restyle_damage(self) -> RestyleDamage; fn set_restyle_damage(self, damage: RestyleDamage); } @@ -24,6 +25,10 @@ impl<'ln> StyledNode for ThreadSafeLayoutNode<'ln> { self.get_css_select_results() } + fn unstyle(self) { + self.remove_css_select_results() + } + fn restyle_damage(self) -> RestyleDamage { self.get_restyle_damage() } diff --git a/components/layout/css/node_util.rs b/components/layout/css/node_util.rs index 17d181b9937..c5268abf169 100644 --- a/components/layout/css/node_util.rs +++ b/components/layout/css/node_util.rs @@ -13,6 +13,7 @@ use sync::Arc; pub trait NodeUtil { fn get_css_select_results<'a>(&'a self) -> &'a Arc<ComputedValues>; fn have_css_select_results(&self) -> bool; + fn remove_css_select_results(self); fn get_restyle_damage(self) -> RestyleDamage; fn set_restyle_damage(self, damage: RestyleDamage); @@ -60,6 +61,20 @@ impl<'ln> NodeUtil for ThreadSafeLayoutNode<'ln> { layout_data_ref.as_ref().unwrap().shared_data.style.is_some() } + fn remove_css_select_results(self) { + let mut layout_data_ref = self.mutate_layout_data(); + let layout_data = layout_data_ref.as_mut().expect("no layout data"); + + let style = + match self.get_pseudo_element_type() { + Before(_) => &mut layout_data.data.before_style, + After (_) => &mut layout_data.data.after_style, + Normal => &mut layout_data.shared_data.style, + }; + + *style = None; + } + /// 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 { diff --git a/components/layout/flow.rs b/components/layout/flow.rs index 8c75cb27eec..5aca4c11bb3 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -32,7 +32,7 @@ use floats::Floats; use flow_list::{FlowList, FlowListIterator, MutFlowListIterator}; use flow_ref::FlowRef; use fragment::{Fragment, TableRowFragment, TableCellFragment}; -use incremental::RestyleDamage; +use incremental::{RestyleDamage, Reflow}; use inline::InlineFlow; use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo}; use parallel::FlowParallelInfo; @@ -432,7 +432,13 @@ pub trait MutableFlowUtils { /// This is called in a bottom-up traversal (specifically, the assign-block-size traversal). /// So, kids have their flow origin already set. In the case of absolute flow kids, they have /// their hypothetical box position already set. - fn collect_static_block_offsets_from_children(&mut self); + fn collect_static_block_offsets_from_children(self); + + fn propagate_restyle_damage(self); + + /// At the moment, reflow isn't idempotent. This function resets this flow + /// (and all its descendants, recursively), and marks them as needing reflow. + fn nonincremental_reset(self); } pub trait MutableOwnedFlowUtils { @@ -589,6 +595,7 @@ impl FlowFlags { /// The Descendants of a flow. /// /// Also, details about their position wrt this flow. +#[deriving(Clone)] pub struct Descendants { /// Links to every descendant. This must be private because it is unsafe to leak `FlowRef`s to /// layout. @@ -1155,9 +1162,9 @@ impl<'a> MutableFlowUtils for &'a mut Flow + 'a { /// assign-block-size traversal). So, kids have their flow origin already set. /// In the case of absolute flow kids, they have their hypothetical box /// position already set. - fn collect_static_block_offsets_from_children(&mut self) { + fn collect_static_block_offsets_from_children(self) { let mut absolute_descendant_block_offsets = Vec::new(); - for kid in mut_base(*self).child_iter() { + for kid in mut_base(self).child_iter() { let mut gives_absolute_offsets = true; if kid.is_block_like() { let kid_block = kid.as_block(); @@ -1189,7 +1196,88 @@ impl<'a> MutableFlowUtils for &'a mut Flow + 'a { } } } - mut_base(*self).abs_descendants.static_block_offsets = absolute_descendant_block_offsets + mut_base(self).abs_descendants.static_block_offsets = absolute_descendant_block_offsets + } + + fn propagate_restyle_damage(self) { + struct DirtyFloats { + left: bool, + right: bool, + } + + fn doit(flow: &mut Flow, down: RestyleDamage, dirty_floats: &mut DirtyFloats) -> RestyleDamage { + match flow.float_clearance() { + clear::none => {} + clear::left => { + (*dirty_floats).left = false; + } + clear::right => { + (*dirty_floats).right = false; + } + clear::both => { + (*dirty_floats).left = false; + (*dirty_floats).right = false; + } + } + + match flow.float_kind() { + float::none => {} + float::left => { + (*dirty_floats).left = true; + } + float::right => { + (*dirty_floats).right = true; + } + } + + let mut my_damage = mut_base(flow).restyle_damage; + my_damage.insert(down); + + if (*dirty_floats).left || (*dirty_floats).right { + my_damage = RestyleDamage::all(); + } + + let down_damage = my_damage.propagate_down(); + + for kid in child_iter(flow) { + my_damage.insert(doit(kid, down_damage, dirty_floats)); + } + + mut_base(flow).restyle_damage = my_damage; + + my_damage.propagate_up() + } + + doit(self, RestyleDamage::empty(), &mut DirtyFloats { left: false, right: false }); + } + + + fn nonincremental_reset(self) { + fn reset_flow(flow: &mut Flow) { + let base = mut_base(flow); + + if !base.restyle_damage.contains(Reflow) { + return + } + + let writing_mode = base.writing_mode; + + base.position = LogicalRect::zero(writing_mode); + base.overflow = LogicalRect::zero(writing_mode); + base.floats = Floats::new(writing_mode); + base.collapsible_margins = CollapsibleMargins::new(); + base.abs_position = Zero::zero(); + base.block_container_explicit_block_size = None; + base.display_list = DisplayList::new(); + base.layers = DList::new(); + base.absolute_position_info = AbsolutePositionInfo::new(writing_mode); + } + + reset_flow(self); + + for child in child_iter(self) { + child.nonincremental_reset(); + } } } diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs index 0eb0f739c69..2ff6d921490 100644 --- a/components/layout/layout_task.rs +++ b/components/layout/layout_task.rs @@ -7,7 +7,7 @@ use css::matching::{ApplicableDeclarations, MatchMethods}; use css::node_style::StyledNode; -use construct::{FlowConstructionResult, NoConstructionResult}; +use construct::FlowConstructionResult; use context::{LayoutContext, SharedLayoutContext}; use flow::{Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils}; use flow::{PreorderFlowTraversal, PostorderFlowTraversal}; @@ -479,14 +479,12 @@ impl LayoutTask { } /// Retrieves the flow tree root from the root node. - fn get_layout_root(&self, node: LayoutNode) -> FlowRef { + fn get_layout_root(&self, node: LayoutNode, layout_context: &LayoutContext) -> FlowRef { let mut layout_data_ref = node.mutate_layout_data(); - let result = match &mut *layout_data_ref { - &Some(ref mut layout_data) => { - mem::replace(&mut layout_data.data.flow_construction_result, NoConstructionResult) - } - &None => fail!("no layout data for root node"), - }; + let layout_data = layout_data_ref.as_mut().expect("no layout data for root node"); + + let result = layout_data.data.flow_construction_result.swap_out(layout_context); + let mut flow = match result { FlowConstructionResult(mut flow, abs_descendants) => { // Note: Assuming that the root has display 'static' (as per @@ -499,6 +497,7 @@ impl LayoutTask { } _ => fail!("Flow construction didn't result in a flow at the root of the tree!"), }; + flow.get_mut().mark_as_root(); flow } @@ -657,7 +656,23 @@ impl LayoutTask { } } - self.get_layout_root((*node).clone()) + self.get_layout_root((*node).clone(), &LayoutContext::new(&shared_layout_ctx)) + }); + + profile(time::LayoutRestyleDamagePropagation, + Some((&data.url, data.iframe, self.first_reflow.get())), + self.time_profiler_chan.clone(), + || { + layout_root.get_mut().propagate_restyle_damage(); + }); + + profile(time::LayoutNonIncrementalReset, + Some((&data.url, data.iframe, self.first_reflow.get())), + self.time_profiler_chan.clone(), + || { + if shared_layout_ctx.opts.incremental_layout { + layout_root.get_mut().nonincremental_reset(); + } }); // Verification of the flow tree, which ensures that all nodes were either marked as leaves diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs index 41896ba1fad..b9686e84276 100644 --- a/components/layout/traversal.rs +++ b/components/layout/traversal.rs @@ -132,8 +132,15 @@ impl<'a> PreorderDomTraversal for RecalcStyleForNode<'a> { // Just needs to be wrapped in an option for `match_node`. let some_bf = Some(bf); - if node.is_dirty() { - // First, check to see whether we can share a style with someone. + if node.is_dirty() || node.has_dirty_siblings() { + // Remove existing CSS styles from changed nodes, to force + // non-incremental reflow. + if node.has_changed() { + let node = ThreadSafeLayoutNode::new(&node); + node.unstyle(); + } + + // Check to see whether we can share a style with someone. let style_sharing_candidate_cache = self.layout_context.style_sharing_candidate_cache(); let sharing_result = unsafe { @@ -194,17 +201,31 @@ impl<'a> PostorderDomTraversal for ConstructFlows<'a> { fn process(&self, node: LayoutNode) { // Construct flows for this node. { - let node = ThreadSafeLayoutNode::new(&node); - let mut flow_constructor = FlowConstructor::new(self.layout_context); - flow_constructor.process(&node); + let tnode = ThreadSafeLayoutNode::new(&node); + + // Always re-construct if incremental layout is turned off. + if !self.layout_context.shared.opts.incremental_layout { + unsafe { + node.set_dirty_descendants(true); + } + } + + if node.has_dirty_descendants() { + tnode.set_restyle_damage(RestyleDamage::all()); + debug!("Constructing flow for {}", tnode.debug_id()); + let mut flow_constructor = FlowConstructor::new(self.layout_context); + flow_constructor.process(&tnode); + } // Reset the layout damage in this node. It's been propagated to the // flow by the flow constructor. - node.set_restyle_damage(RestyleDamage::empty()); + tnode.set_restyle_damage(RestyleDamage::empty()); } unsafe { + node.set_changed(false); node.set_dirty(false); + node.set_dirty_siblings(false); node.set_dirty_descendants(false); } diff --git a/components/layout/wrapper.rs b/components/layout/wrapper.rs index 706d8b8d04e..6f1d01dcbf2 100644 --- a/components/layout/wrapper.rs +++ b/components/layout/wrapper.rs @@ -49,7 +49,7 @@ use script::dom::htmlimageelement::{HTMLImageElement, LayoutHTMLImageElementHelp use script::dom::htmlinputelement::{HTMLInputElement, LayoutHTMLInputElementHelpers}; use script::dom::node::{DocumentNodeTypeId, ElementNodeTypeId, Node, NodeTypeId}; use script::dom::node::{LayoutNodeHelpers, RawLayoutNodeHelpers, SharedLayoutData, TextNodeTypeId}; -use script::dom::node::{IsDirty, HasDirtyDescendants}; +use script::dom::node::{HasChanged, IsDirty, HasDirtySiblings, HasDirtyDescendants}; use script::dom::text::Text; use script::layout_interface::LayoutChan; use servo_msg::constellation_msg::{PipelineId, SubpageId}; @@ -267,6 +267,11 @@ impl<'ln> LayoutNode<'ln> { self.parent_node() } } + + pub fn debug_id(self) -> uint { + let opaque: OpaqueNode = OpaqueNodeMethods::from_layout_node(&self); + opaque.to_untrusted_node_address() as uint + } } impl<'ln> TNode<'ln, LayoutElement<'ln>> for LayoutNode<'ln> { @@ -343,6 +348,14 @@ impl<'ln> TNode<'ln, LayoutElement<'ln>> for LayoutNode<'ln> { } } + fn has_changed(self) -> bool { + unsafe { self.node.get_flag(HasChanged) } + } + + unsafe fn set_changed(self, value: bool) { + self.node.set_flag(HasChanged, value) + } + fn is_dirty(self) -> bool { unsafe { self.node.get_flag(IsDirty) } } @@ -351,6 +364,14 @@ impl<'ln> TNode<'ln, LayoutElement<'ln>> for LayoutNode<'ln> { self.node.set_flag(IsDirty, value) } + fn has_dirty_siblings(self) -> bool { + unsafe { self.node.get_flag(HasDirtySiblings) } + } + + unsafe fn set_dirty_siblings(self, value: bool) { + self.node.set_flag(HasDirtySiblings, value); + } + fn has_dirty_descendants(self) -> bool { unsafe { self.node.get_flag(HasDirtyDescendants) } } @@ -668,6 +689,10 @@ impl<'ln> ThreadSafeLayoutNode<'ln> { } } + pub fn debug_id(self) -> uint { + self.node.debug_id() + } + /// Returns the next sibling of this node. Unsafe and private because this can lead to races. unsafe fn next_sibling(&self) -> Option<ThreadSafeLayoutNode<'ln>> { if self.pseudo.is_before() { diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index dc6351c9954..5b2e3191ce6 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -135,18 +135,24 @@ bitflags! { static InDisabledState = 0x04, #[doc = "Specifies whether this node is in enabled state."] static InEnabledState = 0x08, - #[doc = "Specifies whether this node has changed since the last reflow."] - static IsDirty = 0x10, + #[doc = "Specifies whether this node _must_ be reflowed regardless of style differences."] + static HasChanged = 0x10, + #[doc = "Specifies whether this node needs style recalc on next reflow."] + static IsDirty = 0x20, + #[doc = "Specifies whether this node has siblings (inclusive of itself) which \ + changed since the last reflow."] + static HasDirtySiblings = 0x40, #[doc = "Specifies whether this node has descendants (inclusive of itself) which \ have changed since the last reflow."] - static HasDirtyDescendants = 0x20, + static HasDirtyDescendants = 0x80, } } impl NodeFlags { pub fn new(type_id: NodeTypeId) -> NodeFlags { + let dirty = HasChanged | IsDirty | HasDirtySiblings | HasDirtyDescendants; match type_id { - DocumentNodeTypeId => IsInDoc | IsDirty, + DocumentNodeTypeId => IsInDoc | dirty, // The following elements are enabled by default. ElementNodeTypeId(HTMLButtonElementTypeId) | ElementNodeTypeId(HTMLInputElementTypeId) | @@ -155,8 +161,8 @@ impl NodeFlags { ElementNodeTypeId(HTMLOptGroupElementTypeId) | ElementNodeTypeId(HTMLOptionElementTypeId) | //ElementNodeTypeId(HTMLMenuItemElementTypeId) | - ElementNodeTypeId(HTMLFieldSetElementTypeId) => InEnabledState | IsDirty, - _ => IsDirty, + ElementNodeTypeId(HTMLFieldSetElementTypeId) => InEnabledState | dirty, + _ => dirty, } } } @@ -414,9 +420,15 @@ pub trait NodeHelpers<'a> { fn get_enabled_state(self) -> bool; fn set_enabled_state(self, state: bool); + fn get_has_changed(self) -> bool; + fn set_has_changed(self, state: bool); + fn get_is_dirty(self) -> bool; fn set_is_dirty(self, state: bool); + fn get_has_dirty_siblings(self) -> bool; + fn set_has_dirty_siblings(self, state: bool); + fn get_has_dirty_descendants(self) -> bool; fn set_has_dirty_descendants(self, state: bool); @@ -574,6 +586,14 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> { self.set_flag(InEnabledState, state) } + fn get_has_changed(self) -> bool { + self.get_flag(HasChanged) + } + + fn set_has_changed(self, state: bool) { + self.set_flag(HasChanged, state) + } + fn get_is_dirty(self) -> bool { self.get_flag(IsDirty) } @@ -582,6 +602,14 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> { self.set_flag(IsDirty, state) } + fn get_has_dirty_siblings(self) -> bool { + self.get_flag(HasDirtySiblings) + } + + fn set_has_dirty_siblings(self, state: bool) { + self.set_flag(HasDirtySiblings, state) + } + fn get_has_dirty_descendants(self) -> bool { self.get_flag(HasDirtyDescendants) } @@ -591,24 +619,24 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> { } fn dirty(self) { - // 1. Dirty descendants. + // 1. Dirty self. + self.set_has_changed(true); + + // 2. Dirty descendants. fn dirty_subtree(node: JSRef<Node>) { - node.set_is_dirty(true); + // Stop if this subtree is already dirty. + if node.get_is_dirty() { return } - let mut has_dirty_descendants = false; + node.set_flag(IsDirty | HasDirtySiblings | HasDirtyDescendants, true); for kid in node.children() { dirty_subtree(kid); - has_dirty_descendants = true; - } - - if has_dirty_descendants { - node.set_has_dirty_descendants(true); } } + dirty_subtree(self); - // 2. Dirty siblings. + // 3. Dirty siblings. // // TODO(cgaebel): This is a very conservative way to account for sibling // selectors. Maybe we can do something smarter in the future. @@ -619,10 +647,10 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> { }; for sibling in parent.root().children() { - sibling.set_is_dirty(true); + sibling.set_has_dirty_siblings(true); } - // 3. Dirty ancestors. + // 4. Dirty ancestors. for ancestor in self.ancestors() { if ancestor.get_has_dirty_descendants() { break } ancestor.set_has_dirty_descendants(true); @@ -2249,9 +2277,15 @@ impl<'a> style::TNode<'a, JSRef<'a, Element>> for JSRef<'a, Node> { elem.unwrap().html_element_in_html_document() } + fn has_changed(self) -> bool { self.get_has_changed() } + unsafe fn set_changed(self, value: bool) { self.set_has_changed(value) } + fn is_dirty(self) -> bool { self.get_is_dirty() } unsafe fn set_dirty(self, value: bool) { self.set_is_dirty(value) } + fn has_dirty_siblings(self) -> bool { self.get_has_dirty_siblings() } + unsafe fn set_dirty_siblings(self, value: bool) { self.set_has_dirty_siblings(value) } + fn has_dirty_descendants(self) -> bool { self.get_has_dirty_descendants() } unsafe fn set_dirty_descendants(self, value: bool) { self.set_has_dirty_descendants(value) } } diff --git a/components/style/node.rs b/components/style/node.rs index fae199364de..ea0d8a614c0 100644 --- a/components/style/node.rs +++ b/components/style/node.rs @@ -21,9 +21,15 @@ pub trait TNode<'a, E: TElement<'a>> : Clone + Copy { fn match_attr(self, attr: &AttrSelector, test: |&str| -> bool) -> bool; fn is_html_element_in_html_document(self) -> bool; + fn has_changed(self) -> bool; + unsafe fn set_changed(self, value: bool); + fn is_dirty(self) -> bool; unsafe fn set_dirty(self, value: bool); + fn has_dirty_siblings(self) -> bool; + unsafe fn set_dirty_siblings(self, value: bool); + fn has_dirty_descendants(self) -> bool; unsafe fn set_dirty_descendants(self, value: bool); } diff --git a/components/util/opts.rs b/components/util/opts.rs index b5970a1f5c1..cedfa26f7ae 100644 --- a/components/util/opts.rs +++ b/components/util/opts.rs @@ -59,6 +59,8 @@ pub struct Opts { /// sequential algorithm. pub layout_threads: uint, + pub incremental_layout: bool, + /// True to exit after the page load (`-x`). pub exit_after_load: bool, @@ -126,6 +128,7 @@ pub fn from_cmdline_args(args: &[String]) -> Option<Opts> { getopts::optflagopt("m", "memory-profile", "Memory profiler flag and output interval", "10"), getopts::optflag("x", "exit", "Exit after load flag"), getopts::optopt("y", "layout-threads", "Number of threads to use for layout", "1"), + getopts::optflag("i", "incremental-layout", "Whether or not to use incremental layout."), getopts::optflag("z", "headless", "Headless mode"), getopts::optflag("f", "hard-fail", "Exit on task failure instead of displaying about:failure"), getopts::optflag("b", "bubble-widths", "Bubble intrinsic widths separately like other engines"), @@ -208,6 +211,8 @@ pub fn from_cmdline_args(args: &[String]) -> Option<Opts> { None => cmp::max(rt::default_sched_threads() * 3 / 4, 1), }; + let incremental_layout = opt_match.opt_present("i"); + let mut bubble_inline_sizes_separately = opt_match.opt_present("b"); let trace_layout = opt_match.opt_present("trace-layout"); @@ -242,6 +247,7 @@ pub fn from_cmdline_args(args: &[String]) -> Option<Opts> { memory_profiler_period: memory_profiler_period, enable_experimental: opt_match.opt_present("e"), layout_threads: layout_threads, + incremental_layout: incremental_layout, exit_after_load: opt_match.opt_present("x"), output_file: opt_match.opt_str("o"), headless: opt_match.opt_present("z"), diff --git a/components/util/time.rs b/components/util/time.rs index d3e9b5df65d..8ac5bd05943 100644 --- a/components/util/time.rs +++ b/components/util/time.rs @@ -68,6 +68,8 @@ pub enum TimeProfilerCategory { CompositingCategory, LayoutPerformCategory, LayoutStyleRecalcCategory, + LayoutRestyleDamagePropagation, + LayoutNonIncrementalReset, LayoutSelectorMatchCategory, LayoutTreeBuilderCategory, LayoutDamagePropagateCategory, @@ -86,6 +88,8 @@ impl Formatable for TimeProfilerCategory { fn format(&self) -> String { let padding = match *self { LayoutStyleRecalcCategory | + LayoutRestyleDamagePropagation | + LayoutNonIncrementalReset | LayoutMainCategory | LayoutDispListBuildCategory | LayoutShapingCategory | @@ -99,6 +103,8 @@ impl Formatable for TimeProfilerCategory { CompositingCategory => "Compositing", LayoutPerformCategory => "Layout", LayoutStyleRecalcCategory => "Style Recalc", + LayoutRestyleDamagePropagation => "Restyle Damage Propagation", + LayoutNonIncrementalReset => "Non-incremental reset (temporary)", LayoutSelectorMatchCategory => "Selector Matching", LayoutTreeBuilderCategory => "Tree Building", LayoutDamagePropagateCategory => "Damage Propagation", diff --git a/ports/cef/core.rs b/ports/cef/core.rs index f1b6549c2cc..e2c9fe07851 100644 --- a/ports/cef/core.rs +++ b/ports/cef/core.rs @@ -60,6 +60,7 @@ pub extern "C" fn cef_run_message_loop() { memory_profiler_period: None, enable_experimental: false, layout_threads: 1, + incremental_layout: false, //layout_threads: cmp::max(rt::default_sched_threads() * 3 / 4, 1), exit_after_load: false, output_file: None, diff --git a/tests/reftest.rs b/tests/reftest.rs index 3b888a3c3b9..a0857c78c3a 100644 --- a/tests/reftest.rs +++ b/tests/reftest.rs @@ -242,7 +242,7 @@ fn capture(reftest: &Reftest, side: uint) -> (u32, u32, Vec<u8>) { Ok(status) => status, Err(e) => fail!("failed to execute process: {}", e), }; - assert!(retval == ExitStatus(0)); + assert_eq!(retval, ExitStatus(0)); let image = png::load_png(&from_str::<Path>(png_filename.as_slice()).unwrap()).unwrap(); let rgba8_bytes = match image.pixels { |