mirror of
https://github.com/servo/servo.git
synced 2025-08-04 21:20:23 +01:00
layout: Use a flag to record whether we need to perform a line break if
wrapping on newlines rather than searching for a newline character. Since the newline character might have been stripped out during whitespace stripping, this was incorrect. Fixes the "jumpiness" seen on the Google home page, Wikipedia, and many other places.
This commit is contained in:
parent
3d6d102fd1
commit
8741d9e8c4
3 changed files with 47 additions and 13 deletions
|
@ -598,17 +598,26 @@ pub struct ScannedTextFragmentInfo {
|
||||||
/// so that we can restore the range to its original value (before line breaking occurred) when
|
/// so that we can restore the range to its original value (before line breaking occurred) when
|
||||||
/// performing incremental reflow.
|
/// performing incremental reflow.
|
||||||
pub range_end_including_stripped_whitespace: CharIndex,
|
pub range_end_including_stripped_whitespace: CharIndex,
|
||||||
|
|
||||||
|
/// Whether a line break is required after this fragment if wrapping on newlines (e.g. if
|
||||||
|
/// `white-space: pre` is in effect).
|
||||||
|
pub requires_line_break_afterward_if_wrapping_on_newlines: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScannedTextFragmentInfo {
|
impl ScannedTextFragmentInfo {
|
||||||
/// Creates the information specific to a scanned text fragment from a range and a text run.
|
/// Creates the information specific to a scanned text fragment from a range and a text run.
|
||||||
pub fn new(run: Arc<Box<TextRun>>, range: Range<CharIndex>, content_size: LogicalSize<Au>)
|
pub fn new(run: Arc<Box<TextRun>>,
|
||||||
|
range: Range<CharIndex>,
|
||||||
|
content_size: LogicalSize<Au>,
|
||||||
|
requires_line_break_afterward_if_wrapping_on_newlines: bool)
|
||||||
-> ScannedTextFragmentInfo {
|
-> ScannedTextFragmentInfo {
|
||||||
ScannedTextFragmentInfo {
|
ScannedTextFragmentInfo {
|
||||||
run: run,
|
run: run,
|
||||||
range: range,
|
range: range,
|
||||||
content_size: content_size,
|
content_size: content_size,
|
||||||
range_end_including_stripped_whitespace: range.end(),
|
range_end_including_stripped_whitespace: range.end(),
|
||||||
|
requires_line_break_afterward_if_wrapping_on_newlines:
|
||||||
|
requires_line_break_afterward_if_wrapping_on_newlines,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -801,7 +810,13 @@ impl Fragment {
|
||||||
let size = LogicalSize::new(self.style.writing_mode,
|
let size = LogicalSize::new(self.style.writing_mode,
|
||||||
split.inline_size,
|
split.inline_size,
|
||||||
self.border_box.size.block);
|
self.border_box.size.block);
|
||||||
let info = box ScannedTextFragmentInfo::new(text_run, split.range, size);
|
let requires_line_break_afterward_if_wrapping_on_newlines =
|
||||||
|
self.requires_line_break_afterward_if_wrapping_on_newlines();
|
||||||
|
let info = box ScannedTextFragmentInfo::new(
|
||||||
|
text_run,
|
||||||
|
split.range,
|
||||||
|
size,
|
||||||
|
requires_line_break_afterward_if_wrapping_on_newlines);
|
||||||
self.transform(size, SpecificFragmentInfo::ScannedText(info))
|
self.transform(size, SpecificFragmentInfo::ScannedText(info))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1417,7 +1432,9 @@ impl Fragment {
|
||||||
// The advance is more than the remaining inline-size, so split here. First, check to
|
// The advance is more than the remaining inline-size, so split here. First, check to
|
||||||
// see if we're going to overflow the line. If so, perform a best-effort split.
|
// see if we're going to overflow the line. If so, perform a best-effort split.
|
||||||
let mut remaining_range = slice.text_run_range();
|
let mut remaining_range = slice.text_run_range();
|
||||||
if inline_start_range.is_empty() {
|
let split_is_empty = inline_start_range.is_empty() &&
|
||||||
|
!self.requires_line_break_afterward_if_wrapping_on_newlines();
|
||||||
|
if split_is_empty {
|
||||||
// We're going to overflow the line.
|
// We're going to overflow the line.
|
||||||
overflowing = true;
|
overflowing = true;
|
||||||
inline_start_range = slice.text_run_range();
|
inline_start_range = slice.text_run_range();
|
||||||
|
@ -1439,7 +1456,7 @@ impl Fragment {
|
||||||
|
|
||||||
// If we failed to find a suitable split point, we're on the verge of overflowing the
|
// If we failed to find a suitable split point, we're on the verge of overflowing the
|
||||||
// line.
|
// line.
|
||||||
if inline_start_range.is_empty() || overflowing {
|
if split_is_empty || overflowing {
|
||||||
// If we've been instructed to retry at character boundaries (probably via
|
// If we've been instructed to retry at character boundaries (probably via
|
||||||
// `overflow-wrap: break-word`), do so.
|
// `overflow-wrap: break-word`), do so.
|
||||||
if flags.contains(RETRY_AT_CHARACTER_BOUNDARIES) {
|
if flags.contains(RETRY_AT_CHARACTER_BOUNDARIES) {
|
||||||
|
@ -1464,7 +1481,9 @@ impl Fragment {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
let inline_start = if !inline_start_range.is_empty() {
|
let split_is_empty = inline_start_range.is_empty() &&
|
||||||
|
!self.requires_line_break_afterward_if_wrapping_on_newlines();
|
||||||
|
let inline_start = if !split_is_empty {
|
||||||
Some(SplitInfo::new(inline_start_range, &**text_fragment_info))
|
Some(SplitInfo::new(inline_start_range, &**text_fragment_info))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -1490,6 +1509,9 @@ impl Fragment {
|
||||||
this_info.range.extend_to(other_info.range_end_including_stripped_whitespace);
|
this_info.range.extend_to(other_info.range_end_including_stripped_whitespace);
|
||||||
this_info.content_size.inline =
|
this_info.content_size.inline =
|
||||||
this_info.run.metrics_for_range(&this_info.range).advance_width;
|
this_info.run.metrics_for_range(&this_info.range).advance_width;
|
||||||
|
this_info.requires_line_break_afterward_if_wrapping_on_newlines =
|
||||||
|
this_info.requires_line_break_afterward_if_wrapping_on_newlines ||
|
||||||
|
other_info.requires_line_break_afterward_if_wrapping_on_newlines;
|
||||||
self.border_box.size.inline = this_info.content_size.inline +
|
self.border_box.size.inline = this_info.content_size.inline +
|
||||||
self.border_padding.inline_start_end();
|
self.border_padding.inline_start_end();
|
||||||
}
|
}
|
||||||
|
@ -1920,10 +1942,7 @@ impl Fragment {
|
||||||
pub fn requires_line_break_afterward_if_wrapping_on_newlines(&self) -> bool {
|
pub fn requires_line_break_afterward_if_wrapping_on_newlines(&self) -> bool {
|
||||||
match self.specific {
|
match self.specific {
|
||||||
SpecificFragmentInfo::ScannedText(ref scanned_text) => {
|
SpecificFragmentInfo::ScannedText(ref scanned_text) => {
|
||||||
!scanned_text.range.is_empty() &&
|
scanned_text.requires_line_break_afterward_if_wrapping_on_newlines
|
||||||
scanned_text.run.text.char_at_reverse(scanned_text.range
|
|
||||||
.end()
|
|
||||||
.get() as usize) == '\n'
|
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ use style::properties::style_structs::Font as FontStyle;
|
||||||
use util::geometry::Au;
|
use util::geometry::Au;
|
||||||
use util::linked_list::split_off_head;
|
use util::linked_list::split_off_head;
|
||||||
use util::logical_geometry::{LogicalSize, WritingMode};
|
use util::logical_geometry::{LogicalSize, WritingMode};
|
||||||
use util::range::Range;
|
use util::range::{Range, RangeIndex};
|
||||||
use util::smallvec::{SmallVec, SmallVec1};
|
use util::smallvec::{SmallVec, SmallVec1};
|
||||||
|
|
||||||
/// A stack-allocated object for scanning an inline flow into `TextRun`-containing `TextFragment`s.
|
/// A stack-allocated object for scanning an inline flow into `TextRun`-containing `TextFragment`s.
|
||||||
|
@ -193,16 +193,25 @@ impl TextRunScanner {
|
||||||
debug!("TextRunScanner: pushing {} fragment(s)", self.clump.len());
|
debug!("TextRunScanner: pushing {} fragment(s)", self.clump.len());
|
||||||
for (logical_offset, old_fragment) in
|
for (logical_offset, old_fragment) in
|
||||||
mem::replace(&mut self.clump, LinkedList::new()).into_iter().enumerate() {
|
mem::replace(&mut self.clump, LinkedList::new()).into_iter().enumerate() {
|
||||||
let range = *new_ranges.get(logical_offset);
|
let mut range = *new_ranges.get(logical_offset);
|
||||||
if range.is_empty() {
|
if range.is_empty() {
|
||||||
debug!("Elided an `SpecificFragmentInfo::UnscannedText` because it was \
|
debug!("Elided an `SpecificFragmentInfo::UnscannedText` because it was \
|
||||||
zero-length after compression");
|
zero-length after compression");
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let requires_line_break_afterward_if_wrapping_on_newlines =
|
||||||
|
run.text.char_at_reverse(range.end().get() as usize) == '\n';
|
||||||
|
if requires_line_break_afterward_if_wrapping_on_newlines {
|
||||||
|
range.extend_by(CharIndex(-1))
|
||||||
|
}
|
||||||
|
|
||||||
let text_size = old_fragment.border_box.size;
|
let text_size = old_fragment.border_box.size;
|
||||||
let mut new_text_fragment_info =
|
let mut new_text_fragment_info = box ScannedTextFragmentInfo::new(
|
||||||
box ScannedTextFragmentInfo::new(run.clone(), range, text_size);
|
run.clone(),
|
||||||
|
range,
|
||||||
|
text_size,
|
||||||
|
requires_line_break_afterward_if_wrapping_on_newlines);
|
||||||
let new_metrics = new_text_fragment_info.run.metrics_for_range(&range);
|
let new_metrics = new_text_fragment_info.run.metrics_for_range(&range);
|
||||||
let bounding_box_size = bounding_box_for_run_metrics(&new_metrics,
|
let bounding_box_size = bounding_box_for_run_metrics(&new_metrics,
|
||||||
old_fragment.style.writing_mode);
|
old_fragment.style.writing_mode);
|
||||||
|
|
6
tests/html/incremental_inline_line_flush_mode.html
Normal file
6
tests/html/incremental_inline_line_flush_mode.html
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<!-- This will become "jumpy" when mousing over "Bar" if trailing newlines are stripped from
|
||||||
|
fragments during inline layout. -->
|
||||||
|
<table><td><div style="white-space: pre">
|
||||||
|
Foo</div></td><td><a href="/">Bar</a></td></table>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue