aboutsummaryrefslogtreecommitdiffstats
path: root/components/layout/block.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/layout/block.rs')
-rw-r--r--components/layout/block.rs405
1 files changed, 225 insertions, 180 deletions
diff --git a/components/layout/block.rs b/components/layout/block.rs
index 295cb7883fd..b0ba90c2742 100644
--- a/components/layout/block.rs
+++ b/components/layout/block.rs
@@ -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,214 +826,224 @@ impl BlockFlow {
margins_may_collapse: MarginsMayCollapseFlag) {
let _scope = layout_debug_scope!("assign_block_size_block_base {:x}", self.base.debug_id());
- // Our current border-box position.
- let mut cur_b = Au(0);
+ if self.base.restyle_damage.contains(Reflow) {
+ // Our current border-box position.
+ let mut cur_b = Au(0);
- // Absolute positioning establishes a block formatting context. Don't propagate floats
- // in or out. (But do propagate them between kids.)
- if self.base.flags.is_absolutely_positioned() ||
- margins_may_collapse != MarginsMayCollapse {
- self.base.floats = Floats::new(self.fragment.style.writing_mode);
- }
+ // Absolute positioning establishes a block formatting context. Don't propagate floats
+ // in or out. (But do propagate them between kids.)
+ if self.base.flags.is_absolutely_positioned() ||
+ margins_may_collapse != MarginsMayCollapse {
+ self.base.floats = Floats::new(self.fragment.style.writing_mode);
+ }
- let mut margin_collapse_info = MarginCollapseInfo::new();
- self.base.floats.translate(LogicalSize::new(
- self.fragment.style.writing_mode, -self.fragment.inline_start_offset(), Au(0)));
-
- // The sum of our block-start border and block-start padding.
- let block_start_offset = self.fragment.border_padding.block_start;
- translate_including_floats(&mut cur_b, block_start_offset, &mut self.base.floats);
-
- let can_collapse_block_start_margin_with_kids =
- margins_may_collapse == MarginsMayCollapse &&
- !self.base.flags.is_absolutely_positioned() &&
- self.fragment.border_padding.block_start == Au(0);
- margin_collapse_info.initialize_block_start_margin(
- &self.fragment,
- can_collapse_block_start_margin_with_kids);
-
- // At this point, `cur_b` is at the content edge of our box. Now iterate over children.
- let mut floats = self.base.floats.clone();
- let mut layers_needed_for_descendants = false;
- for kid in self.base.child_iter() {
- if flow::base(kid).flags.is_absolutely_positioned() {
- // Assume that the *hypothetical box* for an absolute flow starts immediately
- // after the block-end border edge of the previous flow.
- kid.as_block().hypothetical_position.b = cur_b;
- kid.assign_block_size_for_inorder_child_if_necessary(layout_context);
- propagate_layer_flag_from_child(&mut layers_needed_for_descendants, kid);
+ let mut margin_collapse_info = MarginCollapseInfo::new();
+ self.base.floats.translate(LogicalSize::new(
+ self.fragment.style.writing_mode, -self.fragment.inline_start_offset(), Au(0)));
- // Skip the collapsing and float processing for absolute flow kids and continue
- // with the next flow.
- continue
- }
+ // The sum of our block-start border and block-start padding.
+ let block_start_offset = self.fragment.border_padding.block_start;
+ translate_including_floats(&mut cur_b, block_start_offset, &mut self.base.floats);
- // Assign block-size now for the child if it was impacted by floats and we couldn't
- // before.
- flow::mut_base(kid).floats = floats.clone();
- if kid.is_float() {
- flow::mut_base(kid).position.start.b = cur_b;
- {
- let kid_block = kid.as_block();
- kid_block.float.as_mut().unwrap().float_ceiling =
- margin_collapse_info.current_float_ceiling();
- }
- propagate_layer_flag_from_child(&mut layers_needed_for_descendants, kid);
+ let can_collapse_block_start_margin_with_kids =
+ margins_may_collapse == MarginsMayCollapse &&
+ !self.base.flags.is_absolutely_positioned() &&
+ self.fragment.border_padding.block_start == Au(0);
+ margin_collapse_info.initialize_block_start_margin(
+ &self.fragment,
+ can_collapse_block_start_margin_with_kids);
- let need_to_process_child_floats =
+ // At this point, `cur_b` is at the content edge of our box. Now iterate over children.
+ let mut floats = self.base.floats.clone();
+ let mut layers_needed_for_descendants = false;
+ for kid in self.base.child_iter() {
+ if flow::base(kid).flags.is_absolutely_positioned() {
+ // Assume that the *hypothetical box* for an absolute flow starts immediately
+ // after the block-end border edge of the previous flow.
+ kid.as_block().hypothetical_position.b = cur_b;
kid.assign_block_size_for_inorder_child_if_necessary(layout_context);
- assert!(need_to_process_child_floats); // As it was a float itself...
+ propagate_layer_flag_from_child(&mut layers_needed_for_descendants, kid);
- let kid_base = flow::mut_base(kid);
- floats = kid_base.floats.clone();
- continue
- }
+ // Skip the collapsing and float processing for absolute flow kids and continue
+ // with the next flow.
+ continue
+ }
- // 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.)
- if flow::base(kid).flags.clears_floats() {
- flow::mut_base(kid).floats = Floats::new(self.fragment.style.writing_mode)
- }
+ // Assign block-size now for the child if it was impacted by floats and we couldn't
+ // before.
+ flow::mut_base(kid).floats = floats.clone();
+ if kid.is_float() {
+ flow::mut_base(kid).position.start.b = cur_b;
+ {
+ let kid_block = kid.as_block();
+ kid_block.float.as_mut().unwrap().float_ceiling =
+ margin_collapse_info.current_float_ceiling();
+ }
+ propagate_layer_flag_from_child(&mut layers_needed_for_descendants, kid);
- // Lay the child out if this was an in-order traversal.
- let need_to_process_child_floats =
- kid.assign_block_size_for_inorder_child_if_necessary(layout_context);
+ let need_to_process_child_floats =
+ kid.assign_block_size_for_inorder_child_if_necessary(layout_context);
+ assert!(need_to_process_child_floats); // As it was a float itself...
- // Mark flows for layerization if necessary to handle painting order correctly.
- propagate_layer_flag_from_child(&mut layers_needed_for_descendants, kid);
+ let kid_base = flow::mut_base(kid);
+ floats = kid_base.floats.clone();
+ continue
+ }
- // Handle any (possibly collapsed) top margin.
- let delta = margin_collapse_info.advance_block_start_margin(
- &flow::base(kid).collapsible_margins);
- translate_including_floats(&mut cur_b, delta, &mut floats);
+ // 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.)
+ if flow::base(kid).flags.clears_floats() {
+ flow::mut_base(kid).floats = Floats::new(self.fragment.style.writing_mode)
+ }
- // Clear past the floats that came in, if necessary.
- let clearance = match (flow::base(kid).flags.clears_left(),
- flow::base(kid).flags.clears_right()) {
- (false, false) => Au(0),
- (true, false) => floats.clearance(ClearLeft),
- (false, true) => floats.clearance(ClearRight),
- (true, true) => floats.clearance(ClearBoth),
- };
- translate_including_floats(&mut cur_b, clearance, &mut floats);
+ // Lay the child out if this was an in-order traversal.
+ let need_to_process_child_floats =
+ kid.assign_block_size_for_inorder_child_if_necessary(layout_context);
- // At this point, `cur_b` is at the border edge of the child.
- flow::mut_base(kid).position.start.b = cur_b;
+ // Mark flows for layerization if necessary to handle painting order correctly.
+ propagate_layer_flag_from_child(&mut layers_needed_for_descendants, kid);
- // 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()
- }
+ // Handle any (possibly collapsed) top margin.
+ let delta = margin_collapse_info.advance_block_start_margin(
+ &flow::base(kid).collapsible_margins);
+ translate_including_floats(&mut cur_b, delta, &mut floats);
+
+ // Clear past the floats that came in, if necessary.
+ let clearance = match (flow::base(kid).flags.clears_left(),
+ flow::base(kid).flags.clears_right()) {
+ (false, false) => Au(0),
+ (true, false) => floats.clearance(ClearLeft),
+ (false, true) => floats.clearance(ClearRight),
+ (true, true) => floats.clearance(ClearBoth),
+ };
+ translate_including_floats(&mut cur_b, clearance, &mut floats);
- // 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.
- let kid_base = flow::mut_base(kid);
- cur_b = cur_b + kid_base.position.size.block;
+ // At this point, `cur_b` is at the border edge of the child.
+ flow::mut_base(kid).position.start.b = cur_b;
- // Handle any (possibly collapsed) block-end margin.
- let delta =
- margin_collapse_info.advance_block_end_margin(&kid_base.collapsible_margins);
- translate_including_floats(&mut cur_b, delta, &mut floats);
- }
+ // 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()
+ }
- // 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.
- (&mut *self as &mut Flow).collect_static_block_offsets_from_children();
-
- // Add in our block-end margin and compute our collapsible margins.
- let can_collapse_block_end_margin_with_kids =
- margins_may_collapse == MarginsMayCollapse &&
- !self.base.flags.is_absolutely_positioned() &&
- self.fragment.border_padding.block_end == Au(0);
- let (collapsible_margins, delta) =
- margin_collapse_info.finish_and_compute_collapsible_margins(
- &self.fragment,
- can_collapse_block_end_margin_with_kids);
- 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.
- 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);
- block_size = Au::max(screen_size.block, block_size)
- }
+ // 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.
+ let kid_base = flow::mut_base(kid);
+ cur_b = cur_b + kid_base.position.size.block;
+
+ // Handle any (possibly collapsed) block-end margin.
+ let delta =
+ margin_collapse_info.advance_block_end_margin(&kid_base.collapsible_margins);
+ 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).
+ self.base.flags.set_layers_needed_for_descendants(layers_needed_for_descendants);
+
+ // Collect various offsets needed by absolutely positioned descendants.
+ (&mut *self as &mut Flow).collect_static_block_offsets_from_children();
+
+ // Add in our block-end margin and compute our collapsible margins.
+ let can_collapse_block_end_margin_with_kids =
+ margins_may_collapse == MarginsMayCollapse &&
+ !self.base.flags.is_absolutely_positioned() &&
+ self.fragment.border_padding.block_end == Au(0);
+ let (collapsible_margins, delta) =
+ margin_collapse_info.finish_and_compute_collapsible_margins(
+ &self.fragment,
+ can_collapse_block_end_margin_with_kids);
+ self.base.collapsible_margins = collapsible_margins;
+ translate_including_floats(&mut cur_b, delta, &mut floats);
- 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.
- block_size = block_size + floats.clearance(ClearBoth);
- }
+ // 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);
+ block_size = Au::max(screen_size.block, block_size)
+ }
- if self.base.flags.is_absolutely_positioned() {
- // Fixed position layers get layers.
- if self.is_fixed() {
- self.base.flags.set_needs_layer(true)
+ 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 it as clearance.
+ block_size = block_size + floats.clearance(ClearBoth);
}
- // 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;
- return
- }
+ if self.base.flags.is_absolutely_positioned() {
+ // Fixed position layers get layers.
+ if self.is_fixed() {
+ self.base.flags.set_needs_layer(true)
+ }
- // Compute any explicitly-specified block size.
- // Can't use `for` because we assign to `candidate_block_size_iterator.candidate_value`.
- let mut candidate_block_size_iterator = CandidateBSizeIterator::new(
- &self.fragment,
- self.base.block_container_explicit_block_size);
- loop {
- match candidate_block_size_iterator.next() {
- Some(candidate_block_size) => {
- candidate_block_size_iterator.candidate_value = match candidate_block_size {
- Auto => block_size,
- Specified(value) => value
+ // 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;
+ return
+ }
+
+ // Compute any explicitly-specified block size.
+ // Can't use `for` because we assign to `candidate_block_size_iterator.candidate_value`.
+ let mut candidate_block_size_iterator = CandidateBSizeIterator::new(
+ &self.fragment,
+ self.base.block_container_explicit_block_size);
+ loop {
+ match candidate_block_size_iterator.next() {
+ Some(candidate_block_size) => {
+ candidate_block_size_iterator.candidate_value =
+ match candidate_block_size {
+ Auto => block_size,
+ Specified(value) => value
+ }
}
+ None => break,
}
- None => break,
}
- }
- // Adjust `cur_b` as necessary to account for the explicitly-specified block-size.
- block_size = candidate_block_size_iterator.candidate_value;
- let delta = block_size - (cur_b - block_start_offset);
- translate_including_floats(&mut cur_b, delta, &mut floats);
+ // Adjust `cur_b` as necessary to account for the explicitly-specified block-size.
+ block_size = candidate_block_size_iterator.candidate_value;
+ let delta = block_size - (cur_b - block_start_offset);
+ translate_including_floats(&mut cur_b, delta, &mut floats);
- // Take border and padding into account.
- let block_end_offset = self.fragment.border_padding.block_end;
- translate_including_floats(&mut cur_b, block_end_offset, &mut floats);
+ // Take border and padding into account.
+ let block_end_offset = self.fragment.border_padding.block_end;
+ translate_including_floats(&mut cur_b, block_end_offset, &mut floats);
- // Now that `cur_b` is at the block-end of the border box, compute the final border box
- // position.
- self.fragment.border_box.size.block = cur_b;
- self.fragment.border_box.start.b = Au(0);
+ // Now that `cur_b` is at the block-end of the border box, compute the final border box
+ // position.
+ self.fragment.border_box.size.block = cur_b;
+ self.fragment.border_box.start.b = Au(0);
- if !self.base.flags.is_absolutely_positioned() {
- self.base.position.size.block = cur_b;
- }
+ if !self.base.flags.is_absolutely_positioned() {
+ self.base.position.size.block = cur_b;
+ }
- // Store the current set of floats in the flow so that flows that come later in the
- // document can access them.
- self.base.floats = floats.clone();
- self.adjust_fragments_for_collapsed_margins_if_root();
+ // Store the current set of floats in the flow so that flows that come later in the
+ // 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() {
- self.assign_block_size(layout_context);
+ 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 {