mirror of
https://github.com/servo/servo.git
synced 2025-06-13 02:44:29 +00:00
layout: Make incremental reflow more fine-grained by introducing "reflow
out-of-flow" and "reconstruct flow" damage bits. This is needed for good performance on the maze solver.
This commit is contained in:
parent
7712052e13
commit
08fc7c2795
20 changed files with 644 additions and 403 deletions
|
@ -36,6 +36,7 @@ use flow::{BaseFlow, BlockFlowClass, FlowClass, Flow, ImmutableFlowUtils};
|
|||
use flow::{MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal, mut_base};
|
||||
use flow;
|
||||
use fragment::{Fragment, ImageFragment, InlineBlockFragment, ScannedTextFragment};
|
||||
use incremental::{Reflow, ReflowOutOfFlow};
|
||||
use layout_debug;
|
||||
use model::{Auto, IntrinsicISizes, MarginCollapseInfo, MarginsCollapse, MarginsCollapseThrough};
|
||||
use model::{MaybeAuto, NoCollapsibleMargins, Specified, specified, specified_or_none};
|
||||
|
@ -51,9 +52,11 @@ use servo_util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize};
|
|||
use servo_util::opts;
|
||||
use std::cmp::{max, min};
|
||||
use std::fmt;
|
||||
use style::ComputedValues;
|
||||
use style::computed_values::{LPA_Auto, LPA_Length, LPA_Percentage, LPN_Length, LPN_None};
|
||||
use style::computed_values::{LPN_Percentage, LP_Length, LP_Percentage, box_sizing, display, float};
|
||||
use style::computed_values::{overflow, position};
|
||||
use sync::Arc;
|
||||
|
||||
/// Information specific to floated blocks.
|
||||
#[deriving(Clone, Encodable)]
|
||||
|
@ -436,6 +439,11 @@ impl<'a> PreorderFlowTraversal for AbsoluteAssignBSizesTraversal<'a> {
|
|||
return;
|
||||
}
|
||||
|
||||
assert!(block_flow.base.flags.is_absolutely_positioned());
|
||||
if !block_flow.base.restyle_damage.intersects(ReflowOutOfFlow | Reflow) {
|
||||
return
|
||||
}
|
||||
|
||||
let AbsoluteAssignBSizesTraversal(ref ctx) = *self;
|
||||
block_flow.calculate_absolute_block_size_and_margins(*ctx);
|
||||
}
|
||||
|
@ -818,6 +826,7 @@ impl BlockFlow {
|
|||
margins_may_collapse: MarginsMayCollapseFlag) {
|
||||
let _scope = layout_debug_scope!("assign_block_size_block_base {:x}", self.base.debug_id());
|
||||
|
||||
if self.base.restyle_damage.contains(Reflow) {
|
||||
// Our current border-box position.
|
||||
let mut cur_b = Au(0);
|
||||
|
||||
|
@ -884,10 +893,10 @@ impl BlockFlow {
|
|||
// If we have clearance, assume there are no floats in.
|
||||
//
|
||||
// FIXME(#2008, pcwalton): This could be wrong if we have `clear: left` or `clear:
|
||||
// right` and there are still floats to impact, of course. But this gets complicated
|
||||
// with margin collapse. Possibly the right thing to do is to lay out the block again
|
||||
// in this rare case. (Note that WebKit can lay blocks out twice; this may be related,
|
||||
// although I haven't looked into it closely.)
|
||||
// right` and there are still floats to impact, of course. But this gets
|
||||
// complicated with margin collapse. Possibly the right thing to do is to lay out
|
||||
// the block again in this rare case. (Note that WebKit can lay blocks out twice;
|
||||
// this may be related, although I haven't looked into it closely.)
|
||||
if flow::base(kid).flags.clears_floats() {
|
||||
flow::mut_base(kid).floats = Floats::new(self.fragment.style.writing_mode)
|
||||
}
|
||||
|
@ -917,15 +926,16 @@ impl BlockFlow {
|
|||
// At this point, `cur_b` is at the border edge of the child.
|
||||
flow::mut_base(kid).position.start.b = cur_b;
|
||||
|
||||
// Now pull out the child's outgoing floats. We didn't do this immediately after the
|
||||
// `assign_block_size_for_inorder_child_if_necessary` call because clearance on a block
|
||||
// operates on the floats that come *in*, not the floats that go *out*.
|
||||
// Now pull out the child's outgoing floats. We didn't do this immediately after
|
||||
// the `assign_block_size_for_inorder_child_if_necessary` call because clearance on
|
||||
// a block operates on the floats that come *in*, not the floats that go *out*.
|
||||
if need_to_process_child_floats {
|
||||
floats = flow::mut_base(kid).floats.clone()
|
||||
}
|
||||
|
||||
// Move past the child's border box. Do not use the `translate_including_floats`
|
||||
// function here because the child has already translated floats past its border box.
|
||||
// function here because the child has already translated floats past its border
|
||||
// box.
|
||||
let kid_base = flow::mut_base(kid);
|
||||
cur_b = cur_b + kid_base.position.size.block;
|
||||
|
||||
|
@ -935,8 +945,8 @@ impl BlockFlow {
|
|||
translate_including_floats(&mut cur_b, delta, &mut floats);
|
||||
}
|
||||
|
||||
// Mark ourselves for layerization if that will be necessary to paint in the proper order
|
||||
// (CSS 2.1, Appendix E).
|
||||
// Mark ourselves for layerization if that will be necessary to paint in the proper
|
||||
// order (CSS 2.1, Appendix E).
|
||||
self.base.flags.set_layers_needed_for_descendants(layers_needed_for_descendants);
|
||||
|
||||
// Collect various offsets needed by absolutely positioned descendants.
|
||||
|
@ -954,22 +964,22 @@ impl BlockFlow {
|
|||
self.base.collapsible_margins = collapsible_margins;
|
||||
translate_including_floats(&mut cur_b, delta, &mut floats);
|
||||
|
||||
// FIXME(#2003, pcwalton): The max is taken here so that you can scroll the page, but this
|
||||
// is not correct behavior according to CSS 2.1 § 10.5. Instead I think we should treat the
|
||||
// root element as having `overflow: scroll` and use the layers-based scrolling
|
||||
// infrastructure to make it scrollable.
|
||||
// FIXME(#2003, pcwalton): The max is taken here so that you can scroll the page, but
|
||||
// this is not correct behavior according to CSS 2.1 § 10.5. Instead I think we should
|
||||
// treat the root element as having `overflow: scroll` and use the layers-based
|
||||
// scrolling infrastructure to make it scrollable.
|
||||
let mut block_size = cur_b - block_start_offset;
|
||||
let is_root = self.is_root();
|
||||
if is_root {
|
||||
let screen_size = LogicalSize::from_physical(
|
||||
self.fragment.style.writing_mode, layout_context.shared.screen_size);
|
||||
let screen_size = LogicalSize::from_physical(self.fragment.style.writing_mode,
|
||||
layout_context.shared.screen_size);
|
||||
block_size = Au::max(screen_size.block, block_size)
|
||||
}
|
||||
|
||||
if is_root || self.formatting_context_type() != NonformattingContext ||
|
||||
self.base.flags.is_absolutely_positioned() {
|
||||
// The content block-size includes all the floats per CSS 2.1 § 10.6.7. The easiest way
|
||||
// to handle this is to just treat this as clearance.
|
||||
// The content block-size includes all the floats per CSS 2.1 § 10.6.7. The easiest
|
||||
// way to handle this is to just treat it as clearance.
|
||||
block_size = block_size + floats.clearance(ClearBoth);
|
||||
}
|
||||
|
||||
|
@ -979,8 +989,8 @@ impl BlockFlow {
|
|||
self.base.flags.set_needs_layer(true)
|
||||
}
|
||||
|
||||
// Store the content block-size for use in calculating the absolute flow's dimensions
|
||||
// later.
|
||||
// Store the content block-size for use in calculating the absolute flow's
|
||||
// dimensions later.
|
||||
//
|
||||
// FIXME(pcwalton): This looks not idempotent. Is it?
|
||||
self.fragment.border_box.size.block = block_size;
|
||||
|
@ -995,7 +1005,8 @@ impl BlockFlow {
|
|||
loop {
|
||||
match candidate_block_size_iterator.next() {
|
||||
Some(candidate_block_size) => {
|
||||
candidate_block_size_iterator.candidate_value = match candidate_block_size {
|
||||
candidate_block_size_iterator.candidate_value =
|
||||
match candidate_block_size {
|
||||
Auto => block_size,
|
||||
Specified(value) => value
|
||||
}
|
||||
|
@ -1026,6 +1037,13 @@ impl BlockFlow {
|
|||
// document can access them.
|
||||
self.base.floats = floats.clone();
|
||||
self.adjust_fragments_for_collapsed_margins_if_root();
|
||||
} else {
|
||||
// We don't need to reflow, but we still need to perform in-order traversals if
|
||||
// necessary.
|
||||
for kid in self.base.child_iter() {
|
||||
kid.assign_block_size_for_inorder_child_if_necessary(layout_context);
|
||||
}
|
||||
}
|
||||
|
||||
if self.is_root_of_absolute_flow_tree() {
|
||||
// Assign block-sizes for all flows in this absolute flow tree.
|
||||
|
@ -1038,6 +1056,15 @@ impl BlockFlow {
|
|||
layout_context: layout_context,
|
||||
});
|
||||
}
|
||||
|
||||
// Don't remove the dirty bits yet if we're absolutely-positioned, since our final size
|
||||
// has not been calculated yet. (See `calculate_absolute_block_size_and_margins` for that.)
|
||||
// Also don't remove the dirty bits if we're a block formatting context since our inline
|
||||
// size has not yet been computed. (See `assign_inline_position_for_formatting_context()`.)
|
||||
if !self.base.flags.is_absolutely_positioned() &&
|
||||
self.formatting_context_type() == NonformattingContext {
|
||||
self.base.restyle_damage.remove(ReflowOutOfFlow | Reflow);
|
||||
}
|
||||
}
|
||||
|
||||
/// Add placement information about current float flow for use by the parent.
|
||||
|
@ -1184,6 +1211,8 @@ impl BlockFlow {
|
|||
let block_size = solution.block_size + self.fragment.border_padding.block_start_end();
|
||||
self.fragment.border_box.size.block = block_size;
|
||||
self.base.position.size.block = block_size;
|
||||
|
||||
self.base.restyle_damage.remove(ReflowOutOfFlow | Reflow);
|
||||
}
|
||||
|
||||
// Our inline-size was set to the inline-size of the containing block by the flow's parent.
|
||||
|
@ -1375,6 +1404,10 @@ impl BlockFlow {
|
|||
fn assign_inline_position_for_formatting_context(&mut self) {
|
||||
debug_assert!(self.formatting_context_type() != NonformattingContext);
|
||||
|
||||
if !self.base.restyle_damage.intersects(ReflowOutOfFlow | Reflow) {
|
||||
return
|
||||
}
|
||||
|
||||
let info = PlacementInfo {
|
||||
size: LogicalSize::new(
|
||||
self.fragment.style.writing_mode,
|
||||
|
@ -1504,6 +1537,10 @@ impl Flow for BlockFlow {
|
|||
fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
|
||||
let _scope = layout_debug_scope!("block::assign_inline_sizes {:x}", self.base.debug_id());
|
||||
|
||||
if !self.base.restyle_damage.intersects(ReflowOutOfFlow | Reflow) {
|
||||
return
|
||||
}
|
||||
|
||||
debug!("assign_inline_sizes({}): assigning inline_size for flow",
|
||||
if self.is_float() {
|
||||
"float"
|
||||
|
@ -1576,12 +1613,16 @@ impl Flow for BlockFlow {
|
|||
}
|
||||
|
||||
let is_formatting_context = self.formatting_context_type() != NonformattingContext;
|
||||
if is_formatting_context {
|
||||
if !self.base.flags.is_absolutely_positioned() && is_formatting_context {
|
||||
self.assign_inline_position_for_formatting_context();
|
||||
}
|
||||
|
||||
if self.base.flags.impacted_by_floats() {
|
||||
if self.base.restyle_damage.intersects(ReflowOutOfFlow | Reflow) {
|
||||
self.assign_block_size(layout_context);
|
||||
// Don't remove the restyle damage; `assign_block_size` decides whether that is
|
||||
// appropriate (which in the case of e.g. absolutely-positioned flows, it is not).
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -1771,6 +1812,10 @@ impl Flow for BlockFlow {
|
|||
self.base.validate_display_list_geometry();
|
||||
}
|
||||
}
|
||||
|
||||
fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
|
||||
self.fragment.repair_style(new_style)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Show for BlockFlow {
|
||||
|
|
|
@ -27,7 +27,7 @@ use fragment::{InlineAbsoluteHypotheticalFragmentInfo, InlineBlockFragment};
|
|||
use fragment::{InlineBlockFragmentInfo, InputFragment, SpecificFragmentInfo, TableCellFragment};
|
||||
use fragment::{TableColumnFragment, TableColumnFragmentInfo, TableFragment, TableRowFragment};
|
||||
use fragment::{TableWrapperFragment, UnscannedTextFragment, UnscannedTextFragmentInfo};
|
||||
use incremental::RestyleDamage;
|
||||
use incremental::{ReconstructFlow, RestyleDamage};
|
||||
use inline::InlineFlow;
|
||||
use parallel;
|
||||
use table_wrapper::TableWrapperFlow;
|
||||
|
@ -38,7 +38,7 @@ use table_rowgroup::TableRowGroupFlow;
|
|||
use table_row::TableRowFlow;
|
||||
use table_cell::TableCellFlow;
|
||||
use text::TextRunScanner;
|
||||
use util::{LayoutDataAccess, OpaqueNodeMethods, LayoutDataWrapper};
|
||||
use util::{HasNewlyConstructedFlow, LayoutDataAccess, OpaqueNodeMethods, LayoutDataWrapper};
|
||||
use wrapper::{PostorderNodeMutTraversal, TLayoutNode, ThreadSafeLayoutNode};
|
||||
use wrapper::{Before, After, Normal};
|
||||
|
||||
|
@ -942,6 +942,44 @@ impl<'a> FlowConstructor<'a> {
|
|||
|
||||
FlowConstructionResult(flow, Descendants::new())
|
||||
}
|
||||
|
||||
/// Attempts to perform incremental repair to account for recent changes to this node. This
|
||||
/// can fail and return false, indicating that flows will need to be reconstructed.
|
||||
///
|
||||
/// TODO(pcwalton): Add some more fast paths, like toggling `display: none`, adding block kids
|
||||
/// to block parents with no {ib} splits, adding out-of-flow kids, etc.
|
||||
pub fn repair_if_possible(&mut self, node: &ThreadSafeLayoutNode) -> bool {
|
||||
// We can skip reconstructing the flow if we don't have to reconstruct and none of our kids
|
||||
// did either.
|
||||
if node.restyle_damage().contains(ReconstructFlow) {
|
||||
return false
|
||||
}
|
||||
|
||||
let mut need_to_reconstruct = false;
|
||||
for kid in node.children() {
|
||||
if kid.flags().contains(HasNewlyConstructedFlow) {
|
||||
kid.remove_flags(HasNewlyConstructedFlow);
|
||||
need_to_reconstruct = true
|
||||
}
|
||||
}
|
||||
if need_to_reconstruct {
|
||||
return false
|
||||
}
|
||||
|
||||
match node.swap_out_construction_result() {
|
||||
NoConstructionResult => true,
|
||||
FlowConstructionResult(mut flow, _) => {
|
||||
// The node's flow is of the same type and has the same set of children and can
|
||||
// therefore be repaired by simply propagating damage and style to the flow.
|
||||
flow::mut_base(&mut *flow).restyle_damage.insert(node.restyle_damage());
|
||||
flow.repair_style(node.style());
|
||||
true
|
||||
}
|
||||
ConstructionItemConstructionResult(_) => {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> {
|
||||
|
@ -1088,6 +1126,7 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
node.insert_flags(HasNewlyConstructedFlow);
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,11 +5,9 @@
|
|||
//! High-level interface to CSS selector matching.
|
||||
|
||||
use css::node_style::StyledNode;
|
||||
use incremental;
|
||||
use incremental::RestyleDamage;
|
||||
use incremental::{mod, RestyleDamage};
|
||||
use util::{LayoutDataAccess, LayoutDataWrapper};
|
||||
use wrapper::{LayoutElement, LayoutNode, ThreadSafeLayoutNode};
|
||||
use wrapper::{TLayoutNode};
|
||||
use wrapper::{LayoutElement, LayoutNode, TLayoutNode};
|
||||
|
||||
use script::dom::node::{TextNodeTypeId};
|
||||
use servo_util::bloom::BloomFilter;
|
||||
|
@ -339,8 +337,8 @@ pub enum StyleSharingResult<'ln> {
|
|||
/// is shareable at all.
|
||||
CannotShare(bool),
|
||||
/// The node's style can be shared. The integer specifies the index in the LRU cache that was
|
||||
/// hit.
|
||||
StyleWasShared(uint),
|
||||
/// hit and the damage that was done.
|
||||
StyleWasShared(uint, RestyleDamage),
|
||||
}
|
||||
|
||||
pub trait MatchMethods {
|
||||
|
@ -385,7 +383,8 @@ trait PrivateMatchMethods {
|
|||
style: &mut Option<Arc<ComputedValues>>,
|
||||
applicable_declarations_cache: &mut
|
||||
ApplicableDeclarationsCache,
|
||||
shareable: bool) -> RestyleDamage;
|
||||
shareable: bool)
|
||||
-> RestyleDamage;
|
||||
|
||||
fn share_style_with_candidate_if_possible(&self,
|
||||
parent_node: Option<LayoutNode>,
|
||||
|
@ -400,7 +399,8 @@ impl<'ln> PrivateMatchMethods for LayoutNode<'ln> {
|
|||
style: &mut Option<Arc<ComputedValues>>,
|
||||
applicable_declarations_cache: &mut
|
||||
ApplicableDeclarationsCache,
|
||||
shareable: bool) -> RestyleDamage {
|
||||
shareable: bool)
|
||||
-> RestyleDamage {
|
||||
let this_style;
|
||||
let cacheable;
|
||||
match parent_style {
|
||||
|
@ -432,9 +432,10 @@ impl<'ln> PrivateMatchMethods for LayoutNode<'ln> {
|
|||
applicable_declarations_cache.insert(applicable_declarations, this_style.clone());
|
||||
}
|
||||
|
||||
let ret = incremental::compute_damage(&*style, this_style.deref());
|
||||
// Calculate style difference and write.
|
||||
let damage = incremental::compute_damage(style, &*this_style);
|
||||
*style = Some(this_style);
|
||||
ret
|
||||
damage
|
||||
}
|
||||
|
||||
|
||||
|
@ -526,12 +527,11 @@ impl<'ln> MatchMethods for LayoutNode<'ln> {
|
|||
Some(shared_style) => {
|
||||
// Yay, cache hit. Share the style.
|
||||
let mut layout_data_ref = self.mutate_layout_data();
|
||||
let layout_data = layout_data_ref.as_mut().unwrap();
|
||||
let style = &mut layout_data.shared_data.style;
|
||||
layout_data.data.restyle_damage.insert(
|
||||
incremental::compute_damage(&*style, &*shared_style));
|
||||
let shared_data = &mut layout_data_ref.as_mut().unwrap().shared_data;
|
||||
let style = &mut shared_data.style;
|
||||
let damage = incremental::compute_damage(style, &*shared_style);
|
||||
*style = Some(shared_style);
|
||||
return StyleWasShared(i)
|
||||
return StyleWasShared(i, damage)
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
@ -585,8 +585,6 @@ impl<'ln> MatchMethods for LayoutNode<'ln> {
|
|||
parent: Option<LayoutNode>,
|
||||
applicable_declarations: &ApplicableDeclarations,
|
||||
applicable_declarations_cache: &mut ApplicableDeclarationsCache) {
|
||||
let mut restyle_damage = ThreadSafeLayoutNode::new(self).restyle_damage();
|
||||
|
||||
// Get our parent's style. This must be unsafe so that we don't touch the parent's
|
||||
// borrow flags.
|
||||
//
|
||||
|
@ -602,10 +600,10 @@ impl<'ln> MatchMethods for LayoutNode<'ln> {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
{
|
||||
let mut layout_data_ref = self.mutate_layout_data();
|
||||
let layout_data = layout_data_ref.as_mut().expect("no layout_data");
|
||||
match &mut *layout_data_ref {
|
||||
&None => fail!("no layout data"),
|
||||
&Some(ref mut layout_data) => {
|
||||
match self.type_id() {
|
||||
Some(TextNodeTypeId) => {
|
||||
// Text nodes get a copy of the parent style. This ensures
|
||||
|
@ -613,40 +611,35 @@ impl<'ln> MatchMethods for LayoutNode<'ln> {
|
|||
// CSS properties (such as vertical-align) are correctly
|
||||
// set on the fragment(s).
|
||||
let cloned_parent_style = parent_style.unwrap().clone();
|
||||
restyle_damage.insert(
|
||||
incremental::compute_damage(&layout_data.shared_data.style, &*cloned_parent_style));
|
||||
layout_data.shared_data.style = Some(cloned_parent_style);
|
||||
}
|
||||
_ => {
|
||||
restyle_damage.insert(
|
||||
self.cascade_node_pseudo_element(
|
||||
let mut damage = self.cascade_node_pseudo_element(
|
||||
parent_style,
|
||||
applicable_declarations.normal.as_slice(),
|
||||
&mut layout_data.shared_data.style,
|
||||
applicable_declarations_cache,
|
||||
applicable_declarations.normal_shareable));
|
||||
applicable_declarations.normal_shareable);
|
||||
if applicable_declarations.before.len() > 0 {
|
||||
restyle_damage.insert(
|
||||
self.cascade_node_pseudo_element(
|
||||
damage = damage | self.cascade_node_pseudo_element(
|
||||
Some(layout_data.shared_data.style.as_ref().unwrap()),
|
||||
applicable_declarations.before.as_slice(),
|
||||
&mut layout_data.data.before_style,
|
||||
applicable_declarations_cache,
|
||||
false));
|
||||
false);
|
||||
}
|
||||
if applicable_declarations.after.len() > 0 {
|
||||
restyle_damage.insert(
|
||||
self.cascade_node_pseudo_element(
|
||||
damage = damage | self.cascade_node_pseudo_element(
|
||||
Some(layout_data.shared_data.style.as_ref().unwrap()),
|
||||
applicable_declarations.after.as_slice(),
|
||||
&mut layout_data.data.after_style,
|
||||
applicable_declarations_cache,
|
||||
false));
|
||||
false);
|
||||
}
|
||||
layout_data.data.restyle_damage = damage;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ThreadSafeLayoutNode::new(self).set_restyle_damage(restyle_damage);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
// Style retrieval from DOM elements.
|
||||
|
||||
use css::node_util::NodeUtil;
|
||||
use incremental::RestyleDamage;
|
||||
use wrapper::ThreadSafeLayoutNode;
|
||||
|
||||
use style::ComputedValues;
|
||||
|
@ -15,8 +14,6 @@ use sync::Arc;
|
|||
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);
|
||||
}
|
||||
|
||||
impl<'ln> StyledNode for ThreadSafeLayoutNode<'ln> {
|
||||
|
@ -28,15 +25,4 @@ impl<'ln> StyledNode for ThreadSafeLayoutNode<'ln> {
|
|||
fn unstyle(self) {
|
||||
self.remove_css_select_results()
|
||||
}
|
||||
|
||||
fn restyle_damage(self) -> RestyleDamage {
|
||||
self.get_restyle_damage()
|
||||
}
|
||||
|
||||
fn set_restyle_damage(self, damage: RestyleDamage) {
|
||||
fn doit<N: NodeUtil>(n: N, damage: RestyleDamage) {
|
||||
n.set_restyle_damage(damage);
|
||||
}
|
||||
doit(self, damage);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
* 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 incremental::RestyleDamage;
|
||||
use util::LayoutDataAccess;
|
||||
use wrapper::ThreadSafeLayoutNode;
|
||||
use wrapper::{After, Before, Normal};
|
||||
|
@ -15,9 +14,6 @@ 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);
|
||||
}
|
||||
|
||||
impl<'ln> NodeUtil for ThreadSafeLayoutNode<'ln> {
|
||||
|
@ -75,23 +71,5 @@ impl<'ln> NodeUtil for ThreadSafeLayoutNode<'ln> {
|
|||
|
||||
*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 {
|
||||
let layout_data_ref = self.borrow_layout_data();
|
||||
layout_data_ref
|
||||
.as_ref().unwrap()
|
||||
.data
|
||||
.restyle_damage
|
||||
}
|
||||
|
||||
/// Set the restyle damage field.
|
||||
fn set_restyle_damage(self, damage: RestyleDamage) {
|
||||
let mut layout_data_ref = self.mutate_layout_data();
|
||||
match &mut *layout_data_ref {
|
||||
&Some(ref mut layout_data) => layout_data.data.restyle_damage = damage,
|
||||
_ => fail!("no layout data for this node"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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::{ReconstructFlow, Reflow, ReflowOutOfFlow, RestyleDamage};
|
||||
use inline::InlineFlow;
|
||||
use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo};
|
||||
use parallel::FlowParallelInfo;
|
||||
|
@ -62,6 +62,8 @@ use std::raw;
|
|||
use std::sync::atomics::{AtomicUint, SeqCst};
|
||||
use std::slice::MutItems;
|
||||
use style::computed_values::{clear, float, position, text_align};
|
||||
use style::ComputedValues;
|
||||
use sync::Arc;
|
||||
|
||||
/// Virtual methods that make up a float context.
|
||||
///
|
||||
|
@ -199,6 +201,7 @@ pub trait Flow: fmt::Show + ToString + Sync {
|
|||
let impacted = base(&*self).flags.impacted_by_floats();
|
||||
if impacted {
|
||||
self.assign_block_size(layout_context);
|
||||
mut_base(&mut *self).restyle_damage.remove(ReflowOutOfFlow | Reflow);
|
||||
}
|
||||
impacted
|
||||
}
|
||||
|
@ -289,6 +292,10 @@ pub trait Flow: fmt::Show + ToString + Sync {
|
|||
LayerId(pointer, fragment_id)
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to perform incremental fixup of this flow by replacing its fragment's style with
|
||||
/// the new style. This can only succeed if the flow has exactly one fragment.
|
||||
fn repair_style(&mut self, new_style: &Arc<ComputedValues>);
|
||||
}
|
||||
|
||||
impl<'a, E, S: Encoder<E>> Encodable<S, E> for &'a Flow + 'a {
|
||||
|
@ -420,8 +427,6 @@ pub trait MutableFlowUtils {
|
|||
/// 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(self);
|
||||
|
||||
fn propagate_restyle_damage(self);
|
||||
}
|
||||
|
||||
pub trait MutableOwnedFlowUtils {
|
||||
|
@ -874,9 +879,13 @@ impl BaseFlow {
|
|||
}
|
||||
}
|
||||
|
||||
// New flows start out as fully damaged.
|
||||
let mut damage = RestyleDamage::all();
|
||||
damage.remove(ReconstructFlow);
|
||||
|
||||
BaseFlow {
|
||||
ref_count: AtomicUint::new(1),
|
||||
restyle_damage: RestyleDamage::all(),
|
||||
restyle_damage: damage,
|
||||
children: FlowList::new(),
|
||||
intrinsic_inline_sizes: IntrinsicISizes::new(),
|
||||
position: LogicalRect::zero(writing_mode),
|
||||
|
@ -1209,47 +1218,6 @@ impl<'a> MutableFlowUtils for &'a mut Flow + 'a {
|
|||
}
|
||||
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 {
|
||||
if base(flow).flags.clears_left() {
|
||||
dirty_floats.left = false;
|
||||
}
|
||||
if base(flow).flags.clears_right() {
|
||||
dirty_floats.right = false;
|
||||
}
|
||||
|
||||
if base(flow).flags.floats_left() {
|
||||
(*dirty_floats).left = true;
|
||||
} else if base(flow).flags.floats_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 });
|
||||
}
|
||||
}
|
||||
|
||||
impl MutableOwnedFlowUtils for FlowRef {
|
||||
|
|
|
@ -1479,6 +1479,9 @@ impl Fragment {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
|
||||
self.style = (*new_style).clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Show for Fragment {
|
||||
|
|
|
@ -2,8 +2,11 @@
|
|||
* 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 flow::{mod, Flow};
|
||||
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
use style::computed_values::float;
|
||||
use style::ComputedValues;
|
||||
|
||||
bitflags! {
|
||||
|
@ -18,22 +21,67 @@ bitflags! {
|
|||
#[doc = "bottom-up."]
|
||||
static BubbleISizes = 0x02,
|
||||
|
||||
#[doc = "Recompute actual inline-sizes and block-sizes, only taking out-of-flow children \
|
||||
into account. \
|
||||
Propagates up the flow tree because the computation is top-down."]
|
||||
static ReflowOutOfFlow = 0x04,
|
||||
|
||||
#[doc = "Recompute actual inline_sizes and block_sizes."]
|
||||
#[doc = "Propagates up the flow tree because the computation is"]
|
||||
#[doc = "top-down."]
|
||||
static Reflow = 0x04
|
||||
static Reflow = 0x08,
|
||||
|
||||
#[doc = "The entire flow needs to be reconstructed."]
|
||||
static ReconstructFlow = 0x10
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
flags SpecialRestyleDamage: u8 {
|
||||
#[doc="If this flag is set, we need to reflow the entire document. This is more or less a \
|
||||
temporary hack to deal with cases that we don't handle incrementally yet."]
|
||||
static ReflowEntireDocument = 0x01,
|
||||
}
|
||||
}
|
||||
|
||||
impl RestyleDamage {
|
||||
/// Elements of self which should also get set on any ancestor flow.
|
||||
pub fn propagate_up(self) -> RestyleDamage {
|
||||
self & Reflow
|
||||
/// Supposing a flow has the given `position` property and this damage, returns the damage that
|
||||
/// we should add to the *parent* of this flow.
|
||||
pub fn damage_for_parent(self, child_is_absolutely_positioned: bool) -> RestyleDamage {
|
||||
if child_is_absolutely_positioned {
|
||||
self & (Repaint | ReflowOutOfFlow)
|
||||
} else {
|
||||
self & (Repaint | Reflow | ReflowOutOfFlow)
|
||||
}
|
||||
}
|
||||
|
||||
/// Elements of self which should also get set on any child flows.
|
||||
pub fn propagate_down(self) -> RestyleDamage {
|
||||
self & BubbleISizes
|
||||
/// Supposing the *parent* of a flow with the given `position` property has this damage,
|
||||
/// returns the damage that we should add to this flow.
|
||||
pub fn damage_for_child(self,
|
||||
parent_is_absolutely_positioned: bool,
|
||||
child_is_absolutely_positioned: bool)
|
||||
-> RestyleDamage {
|
||||
match (parent_is_absolutely_positioned, child_is_absolutely_positioned) {
|
||||
(false, true) => {
|
||||
// Absolute children are out-of-flow and therefore insulated from changes.
|
||||
//
|
||||
// FIXME(pcwalton): Au contraire, if the containing block dimensions change!
|
||||
self & Repaint
|
||||
}
|
||||
(true, false) => {
|
||||
// Changing the position of an absolutely-positioned block requires us to reflow
|
||||
// its kids.
|
||||
if self.contains(ReflowOutOfFlow) {
|
||||
self | Reflow
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// TODO(pcwalton): Take floatedness into account.
|
||||
self & (Repaint | Reflow)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,7 +92,9 @@ impl fmt::Show for RestyleDamage {
|
|||
let to_iter =
|
||||
[ (Repaint, "Repaint")
|
||||
, (BubbleISizes, "BubbleISizes")
|
||||
, (ReflowOutOfFlow, "ReflowOutOfFlow")
|
||||
, (Reflow, "Reflow")
|
||||
, (ReconstructFlow, "ReconstructFlow")
|
||||
];
|
||||
|
||||
for &(damage, damage_str) in to_iter.iter() {
|
||||
|
@ -78,7 +128,7 @@ macro_rules! add_if_not_equal(
|
|||
pub fn compute_damage(old: &Option<Arc<ComputedValues>>, new: &ComputedValues) -> RestyleDamage {
|
||||
let old: &ComputedValues =
|
||||
match old.as_ref() {
|
||||
None => return Repaint | BubbleISizes | Reflow,
|
||||
None => return RestyleDamage::all(),
|
||||
Some(cv) => &**cv,
|
||||
};
|
||||
|
||||
|
@ -90,23 +140,83 @@ pub fn compute_damage(old: &Option<Arc<ComputedValues>>, new: &ComputedValues) -
|
|||
|
||||
// FIXME: We can short-circuit more of this.
|
||||
|
||||
add_if_not_equal!(old, new, damage, [ Repaint ],
|
||||
[ get_color.color, get_background.background_color,
|
||||
add_if_not_equal!(old, new, damage,
|
||||
[ Repaint ], [
|
||||
get_color.color, get_background.background_color,
|
||||
get_border.border_top_color, get_border.border_right_color,
|
||||
get_border.border_bottom_color, get_border.border_left_color ]);
|
||||
get_border.border_bottom_color, get_border.border_left_color
|
||||
]);
|
||||
|
||||
add_if_not_equal!(old, new, damage, [ Repaint, BubbleISizes, Reflow ],
|
||||
[ get_border.border_top_width, get_border.border_right_width,
|
||||
add_if_not_equal!(old, new, damage,
|
||||
[ Repaint, ReflowOutOfFlow ], [
|
||||
get_positionoffsets.top, get_positionoffsets.left,
|
||||
get_positionoffsets.right, get_positionoffsets.bottom
|
||||
]);
|
||||
|
||||
add_if_not_equal!(old, new, damage,
|
||||
[ Repaint, BubbleISizes, ReflowOutOfFlow, Reflow ], [
|
||||
get_border.border_top_width, get_border.border_right_width,
|
||||
get_border.border_bottom_width, get_border.border_left_width,
|
||||
get_margin.margin_top, get_margin.margin_right,
|
||||
get_margin.margin_bottom, get_margin.margin_left,
|
||||
get_padding.padding_top, get_padding.padding_right,
|
||||
get_padding.padding_bottom, get_padding.padding_left,
|
||||
get_box.position, get_box.width, get_box.height, get_box.float, get_box.display,
|
||||
get_box.width, get_box.height,
|
||||
get_font.font_family, get_font.font_size, get_font.font_style, get_font.font_weight,
|
||||
get_inheritedtext.text_align, get_text.text_decoration, get_inheritedbox.line_height ]);
|
||||
get_inheritedtext.text_align, get_text.text_decoration, get_inheritedbox.line_height
|
||||
]);
|
||||
|
||||
add_if_not_equal!(old, new, damage,
|
||||
[ Repaint, BubbleISizes, ReflowOutOfFlow, Reflow, ReconstructFlow ],
|
||||
[ get_box.float, get_box.display, get_box.position ]);
|
||||
|
||||
// FIXME: test somehow that we checked every CSS property
|
||||
|
||||
damage
|
||||
}
|
||||
|
||||
pub trait LayoutDamageComputation {
|
||||
fn compute_layout_damage(self) -> SpecialRestyleDamage;
|
||||
fn reflow_entire_document(self);
|
||||
}
|
||||
|
||||
impl<'a> LayoutDamageComputation for &'a mut Flow+'a {
|
||||
fn compute_layout_damage(self) -> SpecialRestyleDamage {
|
||||
let mut special_damage = SpecialRestyleDamage::empty();
|
||||
let is_absolutely_positioned = flow::base(self).flags.is_absolutely_positioned();
|
||||
|
||||
{
|
||||
let self_base = flow::mut_base(self);
|
||||
for kid in self_base.children.iter_mut() {
|
||||
let child_is_absolutely_positioned =
|
||||
flow::base(kid).flags.is_absolutely_positioned();
|
||||
flow::mut_base(kid).restyle_damage
|
||||
.insert(self_base.restyle_damage.damage_for_child(
|
||||
is_absolutely_positioned,
|
||||
child_is_absolutely_positioned));
|
||||
special_damage.insert(kid.compute_layout_damage());
|
||||
self_base.restyle_damage
|
||||
.insert(flow::base(kid).restyle_damage.damage_for_parent(
|
||||
child_is_absolutely_positioned));
|
||||
}
|
||||
}
|
||||
|
||||
let self_base = flow::base(self);
|
||||
if self_base.flags.float_kind() != float::none &&
|
||||
self_base.restyle_damage.intersects(Reflow) {
|
||||
special_damage.insert(ReflowEntireDocument);
|
||||
}
|
||||
|
||||
special_damage
|
||||
}
|
||||
|
||||
fn reflow_entire_document(self) {
|
||||
let self_base = flow::mut_base(self);
|
||||
self_base.restyle_damage.insert(RestyleDamage::all());
|
||||
self_base.restyle_damage.remove(ReconstructFlow);
|
||||
for kid in self_base.children.iter_mut() {
|
||||
kid.reflow_entire_document();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ use flow::{BaseFlow, FlowClass, Flow, InlineFlowClass, MutableFlowUtils};
|
|||
use flow;
|
||||
use fragment::{Fragment, InlineAbsoluteHypotheticalFragment, InlineBlockFragment};
|
||||
use fragment::{ScannedTextFragment, ScannedTextFragmentInfo, SplitInfo};
|
||||
use incremental::{Reflow, ReflowOutOfFlow};
|
||||
use layout_debug;
|
||||
use model::IntrinsicISizesContribution;
|
||||
use text;
|
||||
|
@ -1111,6 +1112,14 @@ impl Flow for InlineFlow {
|
|||
line.bounds.size.block;
|
||||
} // End of `lines.each` loop.
|
||||
|
||||
// Assign block sizes for any inline-block descendants.
|
||||
for kid in self.base.child_iter() {
|
||||
if flow::base(kid).flags.is_absolutely_positioned() || kid.is_float() {
|
||||
continue
|
||||
}
|
||||
kid.assign_block_size_for_inorder_child_if_necessary(layout_context);
|
||||
}
|
||||
|
||||
self.base.position.size.block = match self.lines.as_slice().last() {
|
||||
Some(ref last_line) => last_line.bounds.start.b + last_line.bounds.size.block,
|
||||
None => Au(0),
|
||||
|
@ -1120,6 +1129,8 @@ impl Flow for InlineFlow {
|
|||
self.base.floats.translate(LogicalSize::new(self.base.writing_mode,
|
||||
Au(0),
|
||||
-self.base.position.size.block));
|
||||
|
||||
self.base.restyle_damage.remove(ReflowOutOfFlow | Reflow);
|
||||
}
|
||||
|
||||
fn compute_absolute_position(&mut self) {
|
||||
|
@ -1210,6 +1221,8 @@ impl Flow for InlineFlow {
|
|||
self.base.validate_display_list_geometry();
|
||||
}
|
||||
}
|
||||
|
||||
fn repair_style(&mut self, _: &Arc<ComputedValues>) {}
|
||||
}
|
||||
|
||||
impl fmt::Show for InlineFlow {
|
||||
|
|
|
@ -8,10 +8,9 @@
|
|||
use css::node_style::StyledNode;
|
||||
use construct::FlowConstructionResult;
|
||||
use context::SharedLayoutContext;
|
||||
use flow::{Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils};
|
||||
use flow;
|
||||
use flow::{mod, Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils};
|
||||
use flow_ref::FlowRef;
|
||||
use incremental::{Reflow, Repaint};
|
||||
use incremental::{LayoutDamageComputation, Reflow, ReflowEntireDocument, Repaint};
|
||||
use layout_debug;
|
||||
use parallel::UnsafeFlow;
|
||||
use parallel;
|
||||
|
@ -675,7 +674,10 @@ impl LayoutTask {
|
|||
Some((&data.url, data.iframe, self.first_reflow.get())),
|
||||
self.time_profiler_chan.clone(),
|
||||
|| {
|
||||
layout_root.propagate_restyle_damage();
|
||||
if opts::get().nonincremental_layout ||
|
||||
layout_root.compute_layout_damage().contains(ReflowEntireDocument) {
|
||||
layout_root.reflow_entire_document()
|
||||
}
|
||||
});
|
||||
|
||||
// Verification of the flow tree, which ensures that all nodes were either marked as leaves
|
||||
|
|
|
@ -22,8 +22,9 @@ use servo_util::geometry::Au;
|
|||
use servo_util::logical_geometry::LogicalRect;
|
||||
use std::cmp::max;
|
||||
use std::fmt;
|
||||
use style::{ComputedValues, CSSFloat};
|
||||
use style::computed_values::{LPA_Auto, LPA_Length, LPA_Percentage, table_layout};
|
||||
use style::CSSFloat;
|
||||
use sync::Arc;
|
||||
|
||||
/// A table flow corresponded to the table's internal table fragment under a table wrapper flow.
|
||||
/// The properties `position`, `float`, and `margin-*` are used on the table wrapper fragment,
|
||||
|
@ -322,6 +323,10 @@ impl Flow for TableFlow {
|
|||
fn build_display_list(&mut self, layout_context: &LayoutContext) {
|
||||
self.block_flow.build_display_list(layout_context);
|
||||
}
|
||||
|
||||
fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
|
||||
self.block_flow.repair_style(new_style)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Show for TableFlow {
|
||||
|
|
|
@ -14,6 +14,8 @@ use wrapper::ThreadSafeLayoutNode;
|
|||
|
||||
use servo_util::geometry::Au;
|
||||
use std::fmt;
|
||||
use style::ComputedValues;
|
||||
use sync::Arc;
|
||||
|
||||
/// A table formatting context.
|
||||
pub struct TableCaptionFlow {
|
||||
|
@ -73,6 +75,10 @@ impl Flow for TableCaptionFlow {
|
|||
debug!("build_display_list_table_caption: same process as block flow");
|
||||
self.block_flow.build_display_list(layout_context)
|
||||
}
|
||||
|
||||
fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
|
||||
self.block_flow.repair_style(new_style)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Show for TableCaptionFlow {
|
||||
|
|
|
@ -17,6 +17,8 @@ use wrapper::ThreadSafeLayoutNode;
|
|||
|
||||
use servo_util::geometry::Au;
|
||||
use std::fmt;
|
||||
use style::ComputedValues;
|
||||
use sync::Arc;
|
||||
|
||||
/// A table formatting context.
|
||||
#[deriving(Encodable)]
|
||||
|
@ -142,6 +144,10 @@ impl Flow for TableCellFlow {
|
|||
fn build_display_list(&mut self, layout_context: &LayoutContext) {
|
||||
self.block_flow.build_display_list(layout_context)
|
||||
}
|
||||
|
||||
fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
|
||||
self.block_flow.repair_style(new_style)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Show for TableCellFlow {
|
||||
|
|
|
@ -17,6 +17,8 @@ use servo_util::geometry::Au;
|
|||
use std::cmp::max;
|
||||
use std::fmt;
|
||||
use style::computed_values::LengthOrPercentageOrAuto;
|
||||
use style::ComputedValues;
|
||||
use sync::Arc;
|
||||
|
||||
/// A table formatting context.
|
||||
pub struct TableColGroupFlow {
|
||||
|
@ -91,6 +93,8 @@ impl Flow for TableColGroupFlow {
|
|||
|
||||
// Table columns are invisible.
|
||||
fn build_display_list(&mut self, _: &LayoutContext) {}
|
||||
|
||||
fn repair_style(&mut self, _: &Arc<ComputedValues>) {}
|
||||
}
|
||||
|
||||
impl fmt::Show for TableColGroupFlow {
|
||||
|
|
|
@ -21,7 +21,9 @@ use wrapper::ThreadSafeLayoutNode;
|
|||
use servo_util::geometry::Au;
|
||||
use std::cmp::max;
|
||||
use std::fmt;
|
||||
use style::ComputedValues;
|
||||
use style::computed_values::{LPA_Auto, LPA_Length, LPA_Percentage};
|
||||
use sync::Arc;
|
||||
|
||||
/// A single row of a table.
|
||||
#[deriving(Encodable)]
|
||||
|
@ -245,6 +247,10 @@ impl Flow for TableRowFlow {
|
|||
fn build_display_list(&mut self, layout_context: &LayoutContext) {
|
||||
self.block_flow.build_display_list(layout_context)
|
||||
}
|
||||
|
||||
fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
|
||||
self.block_flow.repair_style(new_style)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Show for TableRowFlow {
|
||||
|
|
|
@ -20,6 +20,8 @@ use wrapper::ThreadSafeLayoutNode;
|
|||
|
||||
use servo_util::geometry::Au;
|
||||
use std::fmt;
|
||||
use style::ComputedValues;
|
||||
use sync::Arc;
|
||||
|
||||
/// A table formatting context.
|
||||
#[deriving(Encodable)]
|
||||
|
@ -205,6 +207,10 @@ impl Flow for TableRowGroupFlow {
|
|||
debug!("build_display_list_table_rowgroup: same process as block flow");
|
||||
self.block_flow.build_display_list(layout_context)
|
||||
}
|
||||
|
||||
fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
|
||||
self.block_flow.repair_style(new_style)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Show for TableRowGroupFlow {
|
||||
|
|
|
@ -26,8 +26,9 @@ use wrapper::ThreadSafeLayoutNode;
|
|||
use servo_util::geometry::Au;
|
||||
use std::cmp::{max, min};
|
||||
use std::fmt;
|
||||
use style::CSSFloat;
|
||||
use style::{ComputedValues, CSSFloat};
|
||||
use style::computed_values::table_layout;
|
||||
use sync::Arc;
|
||||
|
||||
#[deriving(Encodable)]
|
||||
pub enum TableLayout {
|
||||
|
@ -329,6 +330,10 @@ impl Flow for TableWrapperFlow {
|
|||
fn build_display_list(&mut self, layout_context: &LayoutContext) {
|
||||
self.block_flow.build_display_list(layout_context)
|
||||
}
|
||||
|
||||
fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
|
||||
self.block_flow.repair_style(new_style)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Show for TableWrapperFlow {
|
||||
|
|
|
@ -8,10 +8,10 @@ use css::node_style::StyledNode;
|
|||
use css::matching::{ApplicableDeclarations, CannotShare, MatchMethods, StyleWasShared};
|
||||
use construct::FlowConstructor;
|
||||
use context::LayoutContext;
|
||||
use flow::{Flow, ImmutableFlowUtils, MutableFlowUtils};
|
||||
use flow::{Flow, MutableFlowUtils};
|
||||
use flow::{PreorderFlowTraversal, PostorderFlowTraversal};
|
||||
use flow;
|
||||
use incremental::{RestyleDamage, BubbleISizes, Reflow};
|
||||
use incremental::{RestyleDamage, BubbleISizes, Reflow, ReflowOutOfFlow};
|
||||
use wrapper::{layout_node_to_unsafe_layout_node, LayoutNode};
|
||||
use wrapper::{PostorderNodeMutTraversal, ThreadSafeLayoutNode, UnsafeLayoutNode};
|
||||
use wrapper::{PreorderDomTraversal, PostorderDomTraversal};
|
||||
|
@ -134,9 +134,10 @@ 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() || node.has_dirty_siblings() {
|
||||
// Remove existing CSS styles from changed nodes, to force
|
||||
// non-incremental reflow.
|
||||
let nonincremental_layout = opts::get().nonincremental_layout;
|
||||
if nonincremental_layout || node.is_dirty() {
|
||||
// Remove existing CSS styles from nodes whose content has changed (e.g. text changed),
|
||||
// to force non-incremental reflow.
|
||||
if node.has_changed() {
|
||||
let node = ThreadSafeLayoutNode::new(&node);
|
||||
node.unstyle();
|
||||
|
@ -161,6 +162,8 @@ impl<'a> PreorderDomTraversal for RecalcStyleForNode<'a> {
|
|||
&some_bf,
|
||||
&mut applicable_declarations,
|
||||
&mut shareable);
|
||||
} else {
|
||||
ThreadSafeLayoutNode::new(&node).set_restyle_damage(RestyleDamage::all())
|
||||
}
|
||||
|
||||
// Perform the CSS cascade.
|
||||
|
@ -175,7 +178,10 @@ impl<'a> PreorderDomTraversal for RecalcStyleForNode<'a> {
|
|||
style_sharing_candidate_cache.insert_if_possible(&node);
|
||||
}
|
||||
}
|
||||
StyleWasShared(index) => style_sharing_candidate_cache.touch(index),
|
||||
StyleWasShared(index, damage) => {
|
||||
style_sharing_candidate_cache.touch(index);
|
||||
ThreadSafeLayoutNode::new(&node).set_restyle_damage(damage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -205,18 +211,16 @@ impl<'a> PostorderDomTraversal for ConstructFlows<'a> {
|
|||
{
|
||||
let tnode = ThreadSafeLayoutNode::new(&node);
|
||||
|
||||
// Always re-construct if incremental layout is turned off.
|
||||
if opts::get().nonincremental_layout {
|
||||
unsafe {
|
||||
node.set_dirty_descendants(true);
|
||||
}
|
||||
}
|
||||
|
||||
if node.has_dirty_descendants() {
|
||||
tnode.set_restyle_damage(RestyleDamage::all());
|
||||
// Always reconstruct if incremental layout is turned off.
|
||||
let nonincremental_layout = opts::get().nonincremental_layout;
|
||||
if nonincremental_layout || node.has_dirty_descendants() {
|
||||
let mut flow_constructor = FlowConstructor::new(self.layout_context);
|
||||
if nonincremental_layout || !flow_constructor.repair_if_possible(&tnode) {
|
||||
flow_constructor.process(&tnode);
|
||||
debug!("Constructed flow for {:x}: {:x}", tnode.debug_id(), tnode.flow_debug_id());
|
||||
debug!("Constructed flow for {:x}: {:x}",
|
||||
tnode.debug_id(),
|
||||
tnode.flow_debug_id());
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the layout damage in this node. It's been propagated to the
|
||||
|
@ -300,18 +304,12 @@ pub struct AssignISizes<'a> {
|
|||
impl<'a> PreorderFlowTraversal for AssignISizes<'a> {
|
||||
#[inline]
|
||||
fn process(&self, flow: &mut Flow) {
|
||||
if flow::base(flow).restyle_damage.contains(Reflow) {
|
||||
flow.assign_inline_sizes(self.layout_context);
|
||||
} else if flow.is_block_like() {
|
||||
let block = flow.as_block();
|
||||
block.propagate_and_compute_used_inline_size(self.layout_context);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn should_process(&self, flow: &mut Flow) -> bool {
|
||||
// TODO(cgaebel): Incremental inline size assignment.
|
||||
flow::base(flow).restyle_damage.contains(Reflow) || true
|
||||
flow::base(flow).restyle_damage.intersects(ReflowOutOfFlow | Reflow)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -326,26 +324,25 @@ pub struct AssignBSizesAndStoreOverflow<'a> {
|
|||
impl<'a> PostorderFlowTraversal for AssignBSizesAndStoreOverflow<'a> {
|
||||
#[inline]
|
||||
fn process(&self, flow: &mut Flow) {
|
||||
if !flow::base(flow).flags.impacted_by_floats() {
|
||||
// Can't do anything with flows impacted by floats until we reach their inorder parent.
|
||||
// NB: We must return without resetting the restyle bits for these, as we haven't actually
|
||||
// reflowed anything!
|
||||
if flow::base(flow).flags.impacted_by_floats() {
|
||||
return
|
||||
}
|
||||
|
||||
flow.assign_block_size(self.layout_context);
|
||||
|
||||
// Skip store-overflow for absolutely positioned flows. That will be
|
||||
// done in a separate traversal.
|
||||
|
||||
if flow::base(flow).restyle_damage.contains(Reflow) {
|
||||
if !flow.is_store_overflow_delayed() {
|
||||
flow.store_overflow(self.layout_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flow::mut_base(flow).restyle_damage.remove(Reflow);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn should_process(&self, flow: &mut Flow) -> bool {
|
||||
// TODO(cgaebel): Incremental block size assignment.
|
||||
flow::base(flow).restyle_damage.contains(Reflow) || true
|
||||
flow::base(flow).restyle_damage.intersects(ReflowOutOfFlow | Reflow)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,9 @@ pub struct PrivateLayoutData {
|
|||
|
||||
/// Information needed during parallel traversals.
|
||||
pub parallel: DomParallelInfo,
|
||||
|
||||
/// Various flags.
|
||||
pub flags: LayoutDataFlags,
|
||||
}
|
||||
|
||||
impl PrivateLayoutData {
|
||||
|
@ -55,10 +58,18 @@ impl PrivateLayoutData {
|
|||
before_flow_construction_result: NoConstructionResult,
|
||||
after_flow_construction_result: NoConstructionResult,
|
||||
parallel: DomParallelInfo::new(),
|
||||
flags: LayoutDataFlags::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
flags LayoutDataFlags: u8 {
|
||||
#[doc="Whether a flow has been newly constructed."]
|
||||
static HasNewlyConstructedFlow = 0x01
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LayoutDataWrapper {
|
||||
pub chan: Option<LayoutChan>,
|
||||
pub shared_data: SharedLayoutData,
|
||||
|
|
|
@ -32,7 +32,9 @@
|
|||
|
||||
use context::SharedLayoutContext;
|
||||
use css::node_style::StyledNode;
|
||||
use util::{LayoutDataAccess, LayoutDataWrapper, PrivateLayoutData, OpaqueNodeMethods};
|
||||
use incremental::RestyleDamage;
|
||||
use util::{LayoutDataAccess, LayoutDataFlags, LayoutDataWrapper, OpaqueNodeMethods};
|
||||
use util::{PrivateLayoutData};
|
||||
|
||||
use gfx::display_list::OpaqueNode;
|
||||
use script::dom::bindings::cell::{Ref, RefMut};
|
||||
|
@ -803,7 +805,17 @@ impl<'ln> ThreadSafeLayoutNode<'ln> {
|
|||
layout_data_wrapper_ref.data.after_style.is_some()
|
||||
}
|
||||
|
||||
/// Borrows the layout data without checking. Fails on a conflicting borrow.
|
||||
#[inline(always)]
|
||||
fn borrow_layout_data_unchecked<'a>(&'a self) -> *const Option<LayoutDataWrapper> {
|
||||
unsafe {
|
||||
mem::transmute(self.get().layout_data_unchecked())
|
||||
}
|
||||
}
|
||||
|
||||
/// Borrows the layout data immutably. Fails on a conflicting borrow.
|
||||
///
|
||||
/// TODO(pcwalton): Make this private. It will let us avoid borrow flag checks in some cases.
|
||||
#[inline(always)]
|
||||
pub fn borrow_layout_data<'a>(&'a self) -> Ref<'a,Option<LayoutDataWrapper>> {
|
||||
unsafe {
|
||||
|
@ -812,6 +824,8 @@ impl<'ln> ThreadSafeLayoutNode<'ln> {
|
|||
}
|
||||
|
||||
/// Borrows the layout data mutably. Fails on a conflicting borrow.
|
||||
///
|
||||
/// TODO(pcwalton): Make this private. It will let us avoid borrow flag checks in some cases.
|
||||
#[inline(always)]
|
||||
pub fn mutate_layout_data<'a>(&'a self) -> RefMut<'a,Option<LayoutDataWrapper>> {
|
||||
unsafe {
|
||||
|
@ -887,6 +901,50 @@ impl<'ln> ThreadSafeLayoutNode<'ln> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the description of how to account for recent style changes.
|
||||
/// This is a simple bitfield and fine to copy by value.
|
||||
pub fn restyle_damage(self) -> RestyleDamage {
|
||||
let layout_data_ref = self.borrow_layout_data();
|
||||
layout_data_ref.as_ref().unwrap().data.restyle_damage
|
||||
}
|
||||
|
||||
/// Set the restyle damage field.
|
||||
pub fn set_restyle_damage(self, damage: RestyleDamage) {
|
||||
let mut layout_data_ref = self.mutate_layout_data();
|
||||
match &mut *layout_data_ref {
|
||||
&Some(ref mut layout_data) => layout_data.data.restyle_damage = damage,
|
||||
_ => fail!("no layout data for this node"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the layout data flags for this node.
|
||||
pub fn flags(self) -> LayoutDataFlags {
|
||||
unsafe {
|
||||
match *self.borrow_layout_data_unchecked() {
|
||||
None => fail!(),
|
||||
Some(ref layout_data) => layout_data.data.flags,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds the given flags to this node.
|
||||
pub fn insert_flags(self, new_flags: LayoutDataFlags) {
|
||||
let mut layout_data_ref = self.mutate_layout_data();
|
||||
match &mut *layout_data_ref {
|
||||
&Some(ref mut layout_data) => layout_data.data.flags.insert(new_flags),
|
||||
_ => fail!("no layout data for this node"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes the given flags from this node.
|
||||
pub fn remove_flags(self, flags: LayoutDataFlags) {
|
||||
let mut layout_data_ref = self.mutate_layout_data();
|
||||
match &mut *layout_data_ref {
|
||||
&Some(ref mut layout_data) => layout_data.data.flags.remove(flags),
|
||||
_ => fail!("no layout data for this node"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ThreadSafeLayoutNodeChildrenIterator<'a> {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue