layout: Refactor inline layout a bit.

This commit is contained in:
Patrick Walton 2014-12-10 23:16:18 -08:00
parent e74a57821f
commit 071d320728
3 changed files with 206 additions and 205 deletions

View file

@ -2090,7 +2090,7 @@ pub trait ISizeAndMarginsComputer {
// recalculated, but this time using the value of 'min-inline-size' as the computed value // recalculated, but this time using the value of 'min-inline-size' as the computed value
// for 'inline-size'. // for 'inline-size'.
let computed_min_inline_size = specified(block.fragment().style().min_inline_size(), let computed_min_inline_size = specified(block.fragment().style().min_inline_size(),
containing_block_inline_size); containing_block_inline_size);
if computed_min_inline_size > solution.inline_size { if computed_min_inline_size > solution.inline_size {
input.computed_inline_size = Specified(computed_min_inline_size); input.computed_inline_size = Specified(computed_min_inline_size);
solution = self.solve_inline_size_constraints(block, &input); solution = self.solve_inline_size_constraints(block, &input);

View file

@ -288,6 +288,9 @@ pub trait Flow: fmt::Show + ToString + Sync {
/// Return the dimensions of the containing block generated by this flow for absolutely- /// Return the dimensions of the containing block generated by this flow for absolutely-
/// positioned descendants. For block flows, this is the padding box. /// positioned descendants. For block flows, this is the padding box.
///
/// NB: Do not change this `&self` to `&mut self` under any circumstances! It has security
/// implications because this can be called on parents concurrently from descendants!
fn generated_containing_block_rect(&self) -> LogicalRect<Au> { fn generated_containing_block_rect(&self) -> LogicalRect<Au> {
panic!("generated_containing_block_position not yet implemented for this flow") panic!("generated_containing_block_position not yet implemented for this flow")
} }

View file

@ -158,26 +158,29 @@ int_range_index! {
struct FragmentIndex(int) struct FragmentIndex(int)
} }
/// The line wrapping mode, controlled by the `white-space` property as described in CSS 2.1 § bitflags! {
/// 16.6. flags InlineReflowFlags: u8 {
#[deriving(PartialEq, Eq)] #[doc="The `white-space: nowrap` property from CSS 2.1 § 16.6 is in effect."]
enum WrapMode { const NO_WRAP_INLINE_REFLOW_FLAG = 0x01
/// `normal`. }
WrapNormally,
/// `nowrap`.
NoWrap,
} }
/// Arranges fragments into lines, splitting them up as necessary.
struct LineBreaker { struct LineBreaker {
/// The floats we need to flow around.
floats: Floats, floats: Floats,
new_fragments: Vec<Fragment>, new_fragments: Vec<Fragment>,
work_list: RingBuf<Fragment>, work_list: RingBuf<Fragment>,
/// The line we're currently working on.
pending_line: Line, pending_line: Line,
/// The lines we've already committed.
lines: Vec<Line>, lines: Vec<Line>,
cur_b: Au, // Current position on the block direction /// The current position in the block direction.
cur_b: Au,
} }
impl LineBreaker { impl LineBreaker {
/// Creates a new `LineBreaker` with a set of floats.
fn new(float_context: Floats) -> LineBreaker { fn new(float_context: Floats) -> LineBreaker {
LineBreaker { LineBreaker {
new_fragments: Vec::new(), new_fragments: Vec::new(),
@ -189,22 +192,19 @@ impl LineBreaker {
}, },
floats: float_context, floats: float_context,
lines: Vec::new(), lines: Vec::new(),
cur_b: Au(0) cur_b: Au(0),
} }
} }
fn floats(&mut self) -> Floats { /// Resets the `LineBreaker` to the initial state it had after a call to `new`.
self.floats.clone()
}
fn reset_scanner(&mut self) { fn reset_scanner(&mut self) {
debug!("Resetting LineBreaker's state for flow.");
self.lines = Vec::new(); self.lines = Vec::new();
self.new_fragments = Vec::new(); self.new_fragments = Vec::new();
self.cur_b = Au(0); self.cur_b = Au(0);
self.reset_line(); self.reset_line();
} }
/// Reinitializes the pending line to blank data.
fn reset_line(&mut self) { fn reset_line(&mut self) {
self.pending_line.range.reset(num::zero(), num::zero()); self.pending_line.range.reset(num::zero(), num::zero());
self.pending_line.bounds = LogicalRect::new(self.floats.writing_mode, self.pending_line.bounds = LogicalRect::new(self.floats.writing_mode,
@ -215,70 +215,80 @@ impl LineBreaker {
self.pending_line.green_zone = LogicalSize::zero(self.floats.writing_mode) self.pending_line.green_zone = LogicalSize::zero(self.floats.writing_mode)
} }
/// Reflows fragments for the given inline flow.
fn scan_for_lines(&mut self, flow: &mut InlineFlow, layout_context: &LayoutContext) { fn scan_for_lines(&mut self, flow: &mut InlineFlow, layout_context: &LayoutContext) {
self.reset_scanner(); self.reset_scanner();
debug!("LineBreaker: scanning for lines, {} fragments", flow.fragments.len());
let mut old_fragments = mem::replace(&mut flow.fragments, InlineFragments::new()); let mut old_fragments = mem::replace(&mut flow.fragments, InlineFragments::new());
self.reflow_fragments(old_fragments.fragments.iter(), flow, layout_context);
{
// Enter a new scope so that `old_fragment_iter`'s borrow is released.
debug!("Scanning for lines. {} fragments.", old_fragments.len());
let mut old_fragment_iter = old_fragments.fragments.iter();
loop {
// acquire the next fragment to lay out from work list or fragment list
let cur_fragment = if self.work_list.is_empty() {
match old_fragment_iter.next() {
None => break,
Some(fragment) => {
debug!("LineBreaker: Working with fragment from flow: b{}",
fragment.debug_id());
(*fragment).clone()
}
}
} else {
let fragment = self.work_list.pop_front().unwrap();
debug!("LineBreaker: Working with fragment from work list: b{}",
fragment.debug_id());
fragment
};
let fragment_was_appended = match cur_fragment.white_space() {
white_space::normal => {
self.try_append_to_line(cur_fragment, flow, layout_context, WrapNormally)
}
white_space::pre => self.try_append_to_line_by_new_line(cur_fragment),
white_space::nowrap => {
self.try_append_to_line(cur_fragment, flow, layout_context, NoWrap)
}
};
if !fragment_was_appended {
debug!("LineBreaker: Fragment wasn't appended, because line {:u} was full.",
self.lines.len());
self.flush_current_line();
} else {
debug!("LineBreaker: appended a fragment to line {:u}", self.lines.len());
}
}
if self.pending_line.range.length() > num::zero() {
debug!("LineBreaker: Partially full line {:u} inline_start at end of scanning.",
self.lines.len());
self.flush_current_line();
}
}
old_fragments.fragments = mem::replace(&mut self.new_fragments, vec![]); old_fragments.fragments = mem::replace(&mut self.new_fragments, vec![]);
flow.fragments = old_fragments; flow.fragments = old_fragments;
flow.lines = mem::replace(&mut self.lines, Vec::new()); flow.lines = mem::replace(&mut self.lines, Vec::new());
} }
fn flush_current_line(&mut self) { /// Reflows the given fragments, which have been plucked out of the inline flow.
debug!("LineBreaker: Flushing line {:u}: {}", fn reflow_fragments<'a,I>(&mut self,
self.lines.len(), self.pending_line); mut old_fragment_iter: I,
flow: &'a InlineFlow,
layout_context: &LayoutContext)
where I: Iterator<&'a Fragment> {
loop {
// Acquire the next fragment to lay out from the work list or fragment list, as
// appropriate.
let fragment = if self.work_list.is_empty() {
match old_fragment_iter.next() {
None => break,
Some(fragment) => {
debug!("LineBreaker: working with fragment from flow: {}", fragment);
(*fragment).clone()
}
}
} else {
debug!("LineBreaker: working with fragment from work list: {}",
self.work_list.front());
self.work_list.pop_front().unwrap()
};
// clear line and add line mapping // Set up our reflow flags.
debug!("LineBreaker: Saving information for flushed line {:u}.", self.lines.len()); let flags = match fragment.style().get_inheritedtext().white_space {
white_space::normal => InlineReflowFlags::empty(),
white_space::pre | white_space::nowrap => NO_WRAP_INLINE_REFLOW_FLAG,
};
// Try to append the fragment, and commit the line (so we can try again with the next
// line) if we couldn't.
match fragment.style().get_inheritedtext().white_space {
white_space::normal | white_space::nowrap => {
if !self.append_fragment_to_line_if_possible(fragment,
flow,
layout_context,
flags) {
self.flush_current_line()
}
}
white_space::pre => {
// FIXME(pcwalton): Surely we can unify
// `append_fragment_to_line_if_possible` and
// `try_append_to_line_by_new_line` by adding another bit in the reflow
// flags.
if !self.try_append_to_line_by_new_line(fragment) {
self.flush_current_line()
}
}
}
}
if !self.pending_line_is_empty() {
debug!("LineBreaker: partially full line {} at end of scanning; committing it",
self.lines.len());
self.flush_current_line()
}
}
/// Commits a line to the list.
fn flush_current_line(&mut self) {
debug!("LineBreaker: flushing line {}: {}", self.lines.len(), self.pending_line);
self.lines.push(self.pending_line); self.lines.push(self.pending_line);
self.cur_b = self.pending_line.bounds.start.b + self.pending_line.bounds.size.block; self.cur_b = self.pending_line.bounds.start.b + self.pending_line.bounds.size.block;
self.reset_line(); self.reset_line();
@ -299,50 +309,48 @@ impl LineBreaker {
/// Computes the position of a line that has only the provided fragment. Returns the bounding /// Computes the position of a line that has only the provided fragment. Returns the bounding
/// rect of the line's green zone (whose origin coincides with the line's origin) and the /// rect of the line's green zone (whose origin coincides with the line's origin) and the
/// actual inline-size of the first fragment after splitting. /// actual inline-size of the first fragment after splitting.
fn initial_line_placement(&self, first_fragment: &Fragment, ceiling: Au, flow: &InlineFlow) fn initial_line_placement(&self,
flow: &InlineFlow,
first_fragment: &Fragment,
ceiling: Au)
-> (LogicalRect<Au>, Au) { -> (LogicalRect<Au>, Au) {
debug!("LineBreaker: Trying to place first fragment of line {}", self.lines.len()); debug!("LineBreaker: trying to place first fragment of line {}; fragment size: {}, \
splittable: {}",
let first_fragment_size = first_fragment.border_box.size; self.lines.len(),
let splittable = first_fragment.can_split(); first_fragment.border_box.size,
debug!("LineBreaker: fragment size: {}, splittable: {}", first_fragment_size, splittable); first_fragment.can_split());
// Initially, pretend a splittable fragment has zero inline-size. We will move it later if // Initially, pretend a splittable fragment has zero inline-size. We will move it later if
// it has nonzero inline-size and that causes problems. // it has nonzero inline-size and that causes problems.
let placement_inline_size = if splittable { let placement_inline_size = if first_fragment.can_split() {
Au(0) Au(0)
} else { } else {
first_fragment_size.inline first_fragment.border_box.size.inline
}; };
let info = PlacementInfo { // Try to place the fragment between floats.
let line_bounds = self.floats.place_between_floats(&PlacementInfo {
size: LogicalSize::new(self.floats.writing_mode, size: LogicalSize::new(self.floats.writing_mode,
placement_inline_size, placement_inline_size,
first_fragment_size.block), first_fragment.border_box.size.block),
ceiling: ceiling, ceiling: ceiling,
max_inline_size: flow.base.position.size.inline, max_inline_size: flow.base.position.size.inline,
kind: FloatLeft, kind: FloatLeft,
}; });
let line_bounds = self.floats.place_between_floats(&info); // Simple case: if the fragment fits, then we can stop here.
if line_bounds.size.inline > first_fragment.border_box.size.inline {
debug!("LineBreaker: found position for line: {} using placement_info: {}", debug!("LineBreaker: fragment fits on line {}", self.lines.len());
line_bounds, return (line_bounds, first_fragment.border_box.size.inline);
info);
// Simple case: if the fragment fits, then we can stop here
if line_bounds.size.inline > first_fragment_size.inline {
debug!("LineBreaker: case=fragment fits");
return (line_bounds, first_fragment_size.inline);
} }
// If not, but we can't split the fragment, then we'll place // If not, but we can't split the fragment, then we'll place the line here and it will
// the line here and it will overflow. // overflow.
if !splittable { if !first_fragment.can_split() {
debug!("LineBreaker: case=line doesn't fit, but is unsplittable"); debug!("LineBreaker: line doesn't fit, but is unsplittable");
} }
(line_bounds, first_fragment_size.inline) (line_bounds, first_fragment.border_box.size.inline)
} }
/// Performs float collision avoidance. This is called when adding a fragment is going to /// Performs float collision avoidance. This is called when adding a fragment is going to
@ -359,17 +367,17 @@ impl LineBreaker {
/// ///
/// Returns false if and only if we should break the line. /// Returns false if and only if we should break the line.
fn avoid_floats(&mut self, fn avoid_floats(&mut self,
in_fragment: Fragment,
flow: &InlineFlow, flow: &InlineFlow,
new_block_size: Au, in_fragment: Fragment,
line_is_empty: bool) new_block_size: Au)
-> bool { -> bool {
debug!("LineBreaker: entering float collision avoider!"); debug!("LineBreaker: entering float collision avoider!");
// First predict where the next line is going to be. // First predict where the next line is going to be.
let this_line_y = self.pending_line.bounds.start.b;
let (next_line, first_fragment_inline_size) = let (next_line, first_fragment_inline_size) =
self.initial_line_placement(&in_fragment, this_line_y, flow); self.initial_line_placement(flow,
&in_fragment,
self.pending_line.bounds.start.b);
let next_green_zone = next_line.size; let next_green_zone = next_line.size;
let new_inline_size = self.pending_line.bounds.size.inline + first_fragment_inline_size; let new_inline_size = self.pending_line.bounds.size.inline + first_fragment_inline_size;
@ -382,7 +390,7 @@ impl LineBreaker {
self.pending_line.bounds.start = next_line.start; self.pending_line.bounds.start = next_line.start;
self.pending_line.green_zone = next_green_zone; self.pending_line.green_zone = next_green_zone;
assert!(!line_is_empty, "Non-terminating line breaking"); debug_assert!(!self.pending_line_is_empty(), "Non-terminating line breaking");
self.work_list.push_front(in_fragment); self.work_list.push_front(in_fragment);
return true return true
} }
@ -392,14 +400,17 @@ impl LineBreaker {
false false
} }
/// Tries to append the given fragment to the line for `pre`-formatted text, splitting it if
/// necessary. Returns true if we successfully pushed the fragment to the line or false if we
/// couldn't.
fn try_append_to_line_by_new_line(&mut self, in_fragment: Fragment) -> bool { fn try_append_to_line_by_new_line(&mut self, in_fragment: Fragment) -> bool {
let no_newline_positions = match in_fragment.newline_positions() { let should_push = match in_fragment.newline_positions() {
None => true, None => true,
Some(ref positions) => positions.is_empty(), Some(ref positions) => positions.is_empty(),
}; };
if no_newline_positions { if should_push {
debug!("LineBreaker: Did not find a new-line character, so pushing the fragment to \ debug!("LineBreaker: did not find a newline character; pushing the fragment to \
the line without splitting."); the line without splitting");
self.push_fragment_to_line(in_fragment); self.push_fragment_to_line(in_fragment);
return true return true
} }
@ -408,7 +419,7 @@ impl LineBreaker {
let (inline_start, inline_end, run) = let (inline_start, inline_end, run) =
in_fragment.find_split_info_by_new_line() in_fragment.find_split_info_by_new_line()
.expect("LineBreaker: This split case makes no sense!"); .expect("LineBreaker: this split case makes no sense!");
let writing_mode = self.floats.writing_mode; let writing_mode = self.floats.writing_mode;
let split_fragment = |split: SplitInfo| { let split_fragment = |split: SplitInfo| {
@ -441,40 +452,35 @@ impl LineBreaker {
false false
} }
/// Tries to append the given fragment to the line, splitting it if necessary. Returns false if /// Tries to append the given fragment to the line, splitting it if necessary. Returns true if
/// and only if we should break the line. /// we successfully pushed the fragment to the line or false if we couldn't.
/// fn append_fragment_to_line_if_possible(&mut self,
/// `wrap_mode` controls whether wrapping happens. fragment: Fragment,
fn try_append_to_line(&mut self, flow: &InlineFlow,
in_fragment: Fragment, layout_context: &LayoutContext,
flow: &InlineFlow, flags: InlineReflowFlags)
layout_context: &LayoutContext, -> bool {
wrap_mode: WrapMode) // Determine initial placement for the fragment if we need to.
-> bool { if self.pending_line_is_empty() {
let line_is_empty = self.pending_line.range.length() == num::zero(); let (line_bounds, _) = self.initial_line_placement(flow, &fragment, self.cur_b);
if line_is_empty {
let (line_bounds, _) = self.initial_line_placement(&in_fragment, self.cur_b, flow);
self.pending_line.bounds.start = line_bounds.start; self.pending_line.bounds.start = line_bounds.start;
self.pending_line.green_zone = line_bounds.size; self.pending_line.green_zone = line_bounds.size;
} }
debug!("LineBreaker: Trying to append fragment to line {:u} (fragment size: {}, green \ debug!("LineBreaker: trying to append to line {} (fragment size: {}, green zone: {}): {}",
zone: {}): {}",
self.lines.len(), self.lines.len(),
in_fragment.border_box.size, fragment.border_box.size,
self.pending_line.green_zone, self.pending_line.green_zone,
in_fragment); fragment);
let green_zone = self.pending_line.green_zone;
// NB: At this point, if `green_zone.inline < self.pending_line.bounds.size.inline` or // NB: At this point, if `green_zone.inline < self.pending_line.bounds.size.inline` or
// `green_zone.block < self.pending_line.bounds.size.block`, then we committed a line that // `green_zone.block < self.pending_line.bounds.size.block`, then we committed a line that
// overlaps with floats. // overlaps with floats.
let green_zone = self.pending_line.green_zone;
let new_block_size = self.new_block_size_for_line(&in_fragment, layout_context); let new_block_size = self.new_block_size_for_line(&fragment, layout_context);
if new_block_size > green_zone.block { if new_block_size > green_zone.block {
// Uh-oh. Float collision imminent. Enter the float collision avoider // Uh-oh. Float collision imminent. Enter the float collision avoider!
return self.avoid_floats(in_fragment, flow, new_block_size, line_is_empty) return self.avoid_floats(flow, fragment, new_block_size)
} }
// If we're not going to overflow the green zone vertically, we might still do so // If we're not going to overflow the green zone vertically, we might still do so
@ -482,89 +488,75 @@ impl LineBreaker {
// it doesn't fit. // it doesn't fit.
let new_inline_size = self.pending_line.bounds.size.inline + let new_inline_size = self.pending_line.bounds.size.inline +
in_fragment.border_box.size.inline; fragment.border_box.size.inline;
if new_inline_size <= green_zone.inline { if new_inline_size <= green_zone.inline {
debug!("LineBreaker: case=fragment fits without splitting"); debug!("LineBreaker: fragment fits without splitting");
self.push_fragment_to_line(in_fragment); self.push_fragment_to_line(fragment);
return true return true
} }
if (!in_fragment.can_split() && line_is_empty) || wrap_mode == NoWrap { // If we can't split the fragment or aren't allowed to because of the wrapping mode, then
// TODO(eatkinson, issue #224): Signal that horizontal overflow happened? // just overflow.
debug!("LineBreaker: case=fragment can't split and line {:u} is empty, so \ if (!fragment.can_split() && self.pending_line_is_empty()) ||
overflowing.", flags.contains(NO_WRAP_INLINE_REFLOW_FLAG) {
debug!("LineBreaker: fragment can't split and line {} is empty, so overflowing",
self.lines.len()); self.lines.len());
self.push_fragment_to_line(in_fragment); self.push_fragment_to_line(fragment);
return true return true
} }
// Split it up!
let available_inline_size = green_zone.inline - self.pending_line.bounds.size.inline; let available_inline_size = green_zone.inline - self.pending_line.bounds.size.inline;
let split = in_fragment.find_split_info_for_inline_size(CharIndex(0), let (inline_start_fragment, inline_end_fragment) =
available_inline_size, match fragment.find_split_info_for_inline_size(CharIndex(0),
line_is_empty); available_inline_size,
match split.map(|(inline_start, inline_end, run)| { self.pending_line_is_empty()) {
let split_fragment = |split: SplitInfo| { None => {
let info = box ScannedTextFragmentInfo::new(run.clone(), debug!("LineBreaker: fragment was unsplittable; deferring to next line: {}",
split.range, fragment);
Vec::new(), self.work_list.push_front(fragment);
in_fragment.border_box.size); return false
let size = LogicalSize::new(self.floats.writing_mode, }
split.inline_size, Some((start_split_info, end_split_info, run)) => {
in_fragment.border_box.size.block); let split_fragment = |split: SplitInfo| {
in_fragment.transform(size, info) let info = box ScannedTextFragmentInfo::new(run.clone(),
split.range,
Vec::new(),
fragment.border_box.size);
let size = LogicalSize::new(self.floats.writing_mode,
split.inline_size,
fragment.border_box.size.block);
fragment.transform(size, info)
};
(start_split_info.map(|x| split_fragment(x)),
end_split_info.map(|x| split_fragment(x)))
}
}; };
(inline_start.map(|x| { // Push the first fragment onto the line we're working on and start off the next line with
debug!("LineBreaker: Left split {}", x); // the second fragment. If there's no second fragment, the next line will start off empty.
split_fragment(x) match (inline_start_fragment, inline_end_fragment) {
}), (Some(inline_start_fragment), Some(inline_end_fragment)) => {
inline_end.map(|x| {
debug!("LineBreaker: Right split {}", x);
split_fragment(x)
}))
}) {
None => {
debug!("LineBreaker: Tried to split unsplittable render fragment! Deferring to \
next line. {}",
in_fragment);
self.work_list.push_front(in_fragment);
false
},
Some((Some(inline_start_fragment), Some(inline_end_fragment))) => {
debug!("LineBreaker: Line break found! Pushing inline_start fragment to line and \
deferring inline_end fragment to next line.");
self.push_fragment_to_line(inline_start_fragment); self.push_fragment_to_line(inline_start_fragment);
self.work_list.push_front(inline_end_fragment); self.work_list.push_front(inline_end_fragment)
true
},
Some((Some(inline_start_fragment), None)) => {
debug!("LineBreaker: Pushing inline_start fragment to line.");
self.push_fragment_to_line(inline_start_fragment);
true
},
Some((None, Some(inline_end_fragment))) => {
debug!("LineBreaker: Pushing inline_end fragment to line.");
self.push_fragment_to_line(inline_end_fragment);
true
},
Some((None, None)) => {
debug!("LineBreaker: Nothing to do.");
true
}, },
(Some(fragment), None) | (None, Some(fragment)) => {
self.push_fragment_to_line(fragment)
}
(None, None) => {}
} }
true
} }
// An unconditional push /// Pushes a fragment to the current line unconditionally.
fn push_fragment_to_line(&mut self, fragment: Fragment) { fn push_fragment_to_line(&mut self, fragment: Fragment) {
debug!("LineBreaker: Pushing fragment {} to line {:u}", if self.pending_line_is_empty() {
fragment.debug_id(),
self.lines.len());
if self.pending_line.range.length() == num::zero() {
assert!(self.new_fragments.len() <= (u16::MAX as uint)); assert!(self.new_fragments.len() <= (u16::MAX as uint));
self.pending_line.range.reset(FragmentIndex(self.new_fragments.len() as int), self.pending_line.range.reset(FragmentIndex(self.new_fragments.len() as int),
num::zero()); num::zero());
} }
self.pending_line.range.extend_by(FragmentIndex(1)); self.pending_line.range.extend_by(FragmentIndex(1));
self.pending_line.bounds.size.inline = self.pending_line.bounds.size.inline + self.pending_line.bounds.size.inline = self.pending_line.bounds.size.inline +
fragment.border_box.size.inline; fragment.border_box.size.inline;
@ -572,6 +564,11 @@ impl LineBreaker {
fragment.border_box.size.block); fragment.border_box.size.block);
self.new_fragments.push(fragment); self.new_fragments.push(fragment);
} }
/// Returns true if the pending line is empty and false otherwise.
fn pending_line_is_empty(&self) -> bool {
self.pending_line.range.length() == num::zero()
}
} }
/// Represents a list of inline fragments, including element ranges. /// Represents a list of inline fragments, including element ranges.
@ -810,12 +807,12 @@ impl InlineFlow {
let fragment = fragments.get_mut(fragment_index.to_uint()); let fragment = fragments.get_mut(fragment_index.to_uint());
let size = fragment.border_box.size; let size = fragment.border_box.size;
fragment.border_box = LogicalRect::new(fragment.style.writing_mode, fragment.border_box = LogicalRect::new(fragment.style.writing_mode,
offset, inline_start_position_for_fragment,
fragment.border_box.start.b, fragment.border_box.start.b,
size.inline, size.inline,
size.block); size.block);
fragment.update_late_computed_inline_position_if_necessary(); fragment.update_late_computed_inline_position_if_necessary();
offset = offset + size.inline; inline_start_position_for_fragment = inline_start_position_for_fragment + size.inline;
} }
} }
@ -987,7 +984,7 @@ impl Flow for InlineFlow {
// TODO(pcwalton): Cache the line scanner? // TODO(pcwalton): Cache the line scanner?
debug!("assign_block_size_inline: floats in: {}", self.base.floats); debug!("assign_block_size_inline: floats in: {}", self.base.floats);
// assign block-size for inline fragments // Assign the block-size for the inline fragments.
let containing_block_block_size = let containing_block_block_size =
self.base.block_container_explicit_block_size.unwrap_or(Au(0)); self.base.block_container_explicit_block_size.unwrap_or(Au(0));
for fragment in self.fragments.fragments.iter_mut() { for fragment in self.fragments.fragments.iter_mut() {
@ -995,24 +992,25 @@ impl Flow for InlineFlow {
containing_block_block_size); containing_block_block_size);
} }
// Reset our state, so that we handle incremental reflow correctly.
//
// TODO(pcwalton): Do something smarter, like Gecko and WebKit?
debug!("lines: {}", self.lines); debug!("lines: {}", self.lines);
self.fragments.merge_broken_lines(); self.fragments.merge_broken_lines();
self.lines = Vec::new(); self.lines = Vec::new();
let scanner_floats = self.base.floats.clone();
let mut scanner = LineBreaker::new(scanner_floats);
scanner.scan_for_lines(self, layout_context);
// All lines use text alignment of the flow. // Perform line breaking.
let text_align = self.base.flags.text_align(); let mut scanner = LineBreaker::new(self.base.floats.clone());
scanner.scan_for_lines(self, layout_context);
// Now, go through each line and lay out the fragments inside. // Now, go through each line and lay out the fragments inside.
let mut line_distance_from_flow_block_start = Au(0); let mut line_distance_from_flow_block_start = Au(0);
for line in self.lines.iter_mut() { for line in self.lines.iter_mut() {
// Lay out fragments in the inline direction. // Lay out fragments in the inline direction.
InlineFlow::set_inline_fragment_positions(&mut self.fragments, line, text_align); InlineFlow::set_inline_fragment_positions(&mut self.fragments,
line,
self.base.flags.text_align());
// Set the block-start position of the current line. // Set the block-start position of the current line.
// `line_height_offset` is updated at the end of the previous loop. // `line_height_offset` is updated at the end of the previous loop.
@ -1112,7 +1110,7 @@ impl Flow for InlineFlow {
largest_depth_below_baseline; largest_depth_below_baseline;
line_distance_from_flow_block_start = line_distance_from_flow_block_start + line_distance_from_flow_block_start = line_distance_from_flow_block_start +
line.bounds.size.block; line.bounds.size.block;
} // End of `lines.each` loop. } // End of `lines.iter_mut()` loop.
// Assign block sizes for any inline-block descendants. // Assign block sizes for any inline-block descendants.
for kid in self.base.child_iter() { for kid in self.base.child_iter() {
@ -1128,7 +1126,7 @@ impl Flow for InlineFlow {
None => Au(0), None => Au(0),
}; };
self.base.floats = scanner.floats(); self.base.floats = scanner.floats.clone();
self.base.floats.translate(LogicalSize::new(self.base.writing_mode, self.base.floats.translate(LogicalSize::new(self.base.writing_mode,
Au(0), Au(0),
-self.base.position.size.block)); -self.base.position.size.block));