layout: Rewrite whitespace stripping.

This patch makes Servo unconditionally strip whitespace before text run
scanning (assuming that the `white-space` property allows it). Whitespace
stripping during reflow is now only used for handling whitespace at the ends of
lines; reflow now never attempts to handle ignorable whitespace.

Many CSS tests pass now. There are some new failures, however.

The following reference tests now fail due to a pre-existing bug whereby
whitespace is used to calculate the position of inline hypothetical boxes for
elements with `display: inline; position: absolute`:

* `absolute-replaced-height-036.htm`
* `vertical-align-sub-001.htm`
* `vertical-align-super-001.htm`

The following reference tests fail due to a pre-existing bug whereby we don't
handle `font-size: 0` properly in inline reflow:

* `font-size-zero-1.htm`
* `font-size-zero-2.htm`

The following reference test fails due to the fact that it relied on our
incorrect insertion of whitespace to make room for the black background:

* `inline-formatting-context-007.htm`
This commit is contained in:
Patrick Walton 2015-08-05 09:28:39 -07:00
parent 9c528c6382
commit ae378a8c3e
34 changed files with 216 additions and 181 deletions

View file

@ -26,6 +26,7 @@ use fragment::{Fragment, GeneratedContentInfo, IframeFragmentInfo};
use fragment::{CanvasFragmentInfo, ImageFragmentInfo, InlineAbsoluteFragmentInfo};
use fragment::{InlineAbsoluteHypotheticalFragmentInfo, TableColumnFragmentInfo};
use fragment::{InlineBlockFragmentInfo, SpecificFragmentInfo, UnscannedTextFragmentInfo};
use fragment::{WhitespaceStrippingResult};
use incremental::{RECONSTRUCT_FLOW, RestyleDamage};
use inline::{InlineFlow, InlineFragmentNodeInfo};
use list_item::{ListItemFlow, ListStyleTypeContent};
@ -58,6 +59,7 @@ use style::computed_values::{caption_side, display, empty_cells, float, list_sty
use style::computed_values::{position};
use style::properties::{self, ComputedValues};
use url::Url;
use util::linked_list;
use util::opts;
/// The results of flow construction for a DOM node.
@ -266,12 +268,6 @@ impl InlineFragmentsAccumulator {
}
}
enum WhitespaceStrippingMode {
None,
FromStart,
FromEnd,
}
/// An object that knows how to create flows.
pub struct FlowConstructor<'a> {
/// The layout context.
@ -414,27 +410,16 @@ impl<'a> FlowConstructor<'a> {
fragment_accumulator: InlineFragmentsAccumulator,
flow: &mut FlowRef,
flow_list: &mut Vec<FlowRef>,
whitespace_stripping: WhitespaceStrippingMode,
node: &ThreadSafeLayoutNode) {
let mut fragments = fragment_accumulator.to_intermediate_inline_fragments();
if fragments.is_empty() {
return
};
match whitespace_stripping {
WhitespaceStrippingMode::None => {}
WhitespaceStrippingMode::FromStart => {
strip_ignorable_whitespace_from_start(&mut fragments.fragments);
if fragments.is_empty() {
return
};
}
WhitespaceStrippingMode::FromEnd => {
strip_ignorable_whitespace_from_end(&mut fragments.fragments);
if fragments.is_empty() {
return
};
}
strip_ignorable_whitespace_from_start(&mut fragments.fragments);
strip_ignorable_whitespace_from_end(&mut fragments.fragments);
if fragments.is_empty() {
return
}
// Build a list of all the inline-block fragments before fragments is moved.
@ -504,8 +489,7 @@ impl<'a> FlowConstructor<'a> {
node: &ThreadSafeLayoutNode,
kid: ThreadSafeLayoutNode,
inline_fragment_accumulator: &mut InlineFragmentsAccumulator,
abs_descendants: &mut Descendants,
first_fragment: &mut bool) {
abs_descendants: &mut Descendants) {
match kid.swap_out_construction_result() {
ConstructionResult::None => {}
ConstructionResult::Flow(mut kid_flow, kid_abs_descendants) => {
@ -527,7 +511,6 @@ impl<'a> FlowConstructor<'a> {
InlineFragmentsAccumulator::new()),
flow,
consecutive_siblings,
WhitespaceStrippingMode::FromStart,
node);
if !consecutive_siblings.is_empty() {
let consecutive_siblings = mem::replace(consecutive_siblings, vec!());
@ -554,15 +537,6 @@ impl<'a> FlowConstructor<'a> {
} = split;
inline_fragment_accumulator.push_all(predecessors);
// If this is the first fragment in flow, then strip ignorable
// whitespace per CSS 2.1 § 9.2.1.1.
let whitespace_stripping = if *first_fragment {
*first_fragment = false;
WhitespaceStrippingMode::FromStart
} else {
WhitespaceStrippingMode::None
};
// Flush any inline fragments that we were gathering up.
debug!("flushing {} inline box(es) to flow A",
inline_fragment_accumulator.fragments.fragments.len());
@ -571,7 +545,6 @@ impl<'a> FlowConstructor<'a> {
InlineFragmentsAccumulator::new()),
flow,
consecutive_siblings,
whitespace_stripping,
node);
// Push the flow generated by the {ib} split onto our list of
@ -625,7 +598,6 @@ impl<'a> FlowConstructor<'a> {
let mut consecutive_siblings = vec!();
inline_fragment_accumulator.fragments.push_all(initial_fragments);
let mut first_fragment = inline_fragment_accumulator.fragments.is_empty();
// List of absolute descendants, in tree order.
let mut abs_descendants = Descendants::new();
@ -640,8 +612,7 @@ impl<'a> FlowConstructor<'a> {
node,
kid,
&mut inline_fragment_accumulator,
&mut abs_descendants,
&mut first_fragment);
&mut abs_descendants);
}
// Perform a final flush of any inline fragments that we were gathering up to handle {ib}
@ -649,7 +620,6 @@ impl<'a> FlowConstructor<'a> {
self.flush_inline_fragments_to_flow_or_list(inline_fragment_accumulator,
&mut flow,
&mut consecutive_siblings,
WhitespaceStrippingMode::FromEnd,
node);
if !consecutive_siblings.is_empty() {
self.generate_anonymous_missing_child(consecutive_siblings, &mut flow, node);
@ -1665,10 +1635,21 @@ pub fn strip_ignorable_whitespace_from_start(this: &mut LinkedList<Fragment>) {
return // Fast path.
}
while !this.is_empty() && this.front().as_ref().unwrap().is_ignorable_whitespace() {
debug!("stripping ignorable whitespace from start");
drop(this.pop_front());
let mut leading_fragments_consisting_of_solely_bidi_control_characters = LinkedList::new();
while !this.is_empty() {
match this.front_mut().as_mut().unwrap().strip_leading_whitespace_if_necessary() {
WhitespaceStrippingResult::RetainFragment => break,
WhitespaceStrippingResult::FragmentContainedOnlyBidiControlCharacters => {
leading_fragments_consisting_of_solely_bidi_control_characters.push_back(
this.pop_front().unwrap())
}
WhitespaceStrippingResult::FragmentContainedOnlyWhitespace => {
this.pop_front();
}
}
}
linked_list::prepend_from(this,
&mut leading_fragments_consisting_of_solely_bidi_control_characters)
}
/// Strips ignorable whitespace from the end of a list of fragments.
@ -1677,10 +1658,20 @@ pub fn strip_ignorable_whitespace_from_end(this: &mut LinkedList<Fragment>) {
return
}
while !this.is_empty() && this.back().as_ref().unwrap().is_ignorable_whitespace() {
debug!("stripping ignorable whitespace from end");
drop(this.pop_back());
let mut trailing_fragments_consisting_of_solely_bidi_control_characters = LinkedList::new();
while !this.is_empty() {
match this.back_mut().as_mut().unwrap().strip_trailing_whitespace_if_necessary() {
WhitespaceStrippingResult::RetainFragment => break,
WhitespaceStrippingResult::FragmentContainedOnlyBidiControlCharacters => {
trailing_fragments_consisting_of_solely_bidi_control_characters.push_front(
this.pop_back().unwrap())
}
WhitespaceStrippingResult::FragmentContainedOnlyWhitespace => {
this.pop_back();
}
}
}
this.append(&mut trailing_fragments_consisting_of_solely_bidi_control_characters);
}
/// If the 'unicode-bidi' property has a value other than 'normal', return the bidi control codes