mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
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:
parent
9c528c6382
commit
ae378a8c3e
34 changed files with 216 additions and 181 deletions
|
@ -2,7 +2,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Copy, Clone)]
|
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||||
pub enum CompressionMode {
|
pub enum CompressionMode {
|
||||||
CompressNone,
|
CompressNone,
|
||||||
CompressWhitespace,
|
CompressWhitespace,
|
||||||
|
|
|
@ -26,6 +26,7 @@ use fragment::{Fragment, GeneratedContentInfo, IframeFragmentInfo};
|
||||||
use fragment::{CanvasFragmentInfo, ImageFragmentInfo, InlineAbsoluteFragmentInfo};
|
use fragment::{CanvasFragmentInfo, ImageFragmentInfo, InlineAbsoluteFragmentInfo};
|
||||||
use fragment::{InlineAbsoluteHypotheticalFragmentInfo, TableColumnFragmentInfo};
|
use fragment::{InlineAbsoluteHypotheticalFragmentInfo, TableColumnFragmentInfo};
|
||||||
use fragment::{InlineBlockFragmentInfo, SpecificFragmentInfo, UnscannedTextFragmentInfo};
|
use fragment::{InlineBlockFragmentInfo, SpecificFragmentInfo, UnscannedTextFragmentInfo};
|
||||||
|
use fragment::{WhitespaceStrippingResult};
|
||||||
use incremental::{RECONSTRUCT_FLOW, RestyleDamage};
|
use incremental::{RECONSTRUCT_FLOW, RestyleDamage};
|
||||||
use inline::{InlineFlow, InlineFragmentNodeInfo};
|
use inline::{InlineFlow, InlineFragmentNodeInfo};
|
||||||
use list_item::{ListItemFlow, ListStyleTypeContent};
|
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::computed_values::{position};
|
||||||
use style::properties::{self, ComputedValues};
|
use style::properties::{self, ComputedValues};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
use util::linked_list;
|
||||||
use util::opts;
|
use util::opts;
|
||||||
|
|
||||||
/// The results of flow construction for a DOM node.
|
/// 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.
|
/// An object that knows how to create flows.
|
||||||
pub struct FlowConstructor<'a> {
|
pub struct FlowConstructor<'a> {
|
||||||
/// The layout context.
|
/// The layout context.
|
||||||
|
@ -414,27 +410,16 @@ impl<'a> FlowConstructor<'a> {
|
||||||
fragment_accumulator: InlineFragmentsAccumulator,
|
fragment_accumulator: InlineFragmentsAccumulator,
|
||||||
flow: &mut FlowRef,
|
flow: &mut FlowRef,
|
||||||
flow_list: &mut Vec<FlowRef>,
|
flow_list: &mut Vec<FlowRef>,
|
||||||
whitespace_stripping: WhitespaceStrippingMode,
|
|
||||||
node: &ThreadSafeLayoutNode) {
|
node: &ThreadSafeLayoutNode) {
|
||||||
let mut fragments = fragment_accumulator.to_intermediate_inline_fragments();
|
let mut fragments = fragment_accumulator.to_intermediate_inline_fragments();
|
||||||
if fragments.is_empty() {
|
if fragments.is_empty() {
|
||||||
return
|
return
|
||||||
};
|
};
|
||||||
|
|
||||||
match whitespace_stripping {
|
|
||||||
WhitespaceStrippingMode::None => {}
|
|
||||||
WhitespaceStrippingMode::FromStart => {
|
|
||||||
strip_ignorable_whitespace_from_start(&mut fragments.fragments);
|
strip_ignorable_whitespace_from_start(&mut fragments.fragments);
|
||||||
if fragments.is_empty() {
|
|
||||||
return
|
|
||||||
};
|
|
||||||
}
|
|
||||||
WhitespaceStrippingMode::FromEnd => {
|
|
||||||
strip_ignorable_whitespace_from_end(&mut fragments.fragments);
|
strip_ignorable_whitespace_from_end(&mut fragments.fragments);
|
||||||
if fragments.is_empty() {
|
if fragments.is_empty() {
|
||||||
return
|
return
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build a list of all the inline-block fragments before fragments is moved.
|
// Build a list of all the inline-block fragments before fragments is moved.
|
||||||
|
@ -504,8 +489,7 @@ impl<'a> FlowConstructor<'a> {
|
||||||
node: &ThreadSafeLayoutNode,
|
node: &ThreadSafeLayoutNode,
|
||||||
kid: ThreadSafeLayoutNode,
|
kid: ThreadSafeLayoutNode,
|
||||||
inline_fragment_accumulator: &mut InlineFragmentsAccumulator,
|
inline_fragment_accumulator: &mut InlineFragmentsAccumulator,
|
||||||
abs_descendants: &mut Descendants,
|
abs_descendants: &mut Descendants) {
|
||||||
first_fragment: &mut bool) {
|
|
||||||
match kid.swap_out_construction_result() {
|
match kid.swap_out_construction_result() {
|
||||||
ConstructionResult::None => {}
|
ConstructionResult::None => {}
|
||||||
ConstructionResult::Flow(mut kid_flow, kid_abs_descendants) => {
|
ConstructionResult::Flow(mut kid_flow, kid_abs_descendants) => {
|
||||||
|
@ -527,7 +511,6 @@ impl<'a> FlowConstructor<'a> {
|
||||||
InlineFragmentsAccumulator::new()),
|
InlineFragmentsAccumulator::new()),
|
||||||
flow,
|
flow,
|
||||||
consecutive_siblings,
|
consecutive_siblings,
|
||||||
WhitespaceStrippingMode::FromStart,
|
|
||||||
node);
|
node);
|
||||||
if !consecutive_siblings.is_empty() {
|
if !consecutive_siblings.is_empty() {
|
||||||
let consecutive_siblings = mem::replace(consecutive_siblings, vec!());
|
let consecutive_siblings = mem::replace(consecutive_siblings, vec!());
|
||||||
|
@ -554,15 +537,6 @@ impl<'a> FlowConstructor<'a> {
|
||||||
} = split;
|
} = split;
|
||||||
inline_fragment_accumulator.push_all(predecessors);
|
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.
|
// Flush any inline fragments that we were gathering up.
|
||||||
debug!("flushing {} inline box(es) to flow A",
|
debug!("flushing {} inline box(es) to flow A",
|
||||||
inline_fragment_accumulator.fragments.fragments.len());
|
inline_fragment_accumulator.fragments.fragments.len());
|
||||||
|
@ -571,7 +545,6 @@ impl<'a> FlowConstructor<'a> {
|
||||||
InlineFragmentsAccumulator::new()),
|
InlineFragmentsAccumulator::new()),
|
||||||
flow,
|
flow,
|
||||||
consecutive_siblings,
|
consecutive_siblings,
|
||||||
whitespace_stripping,
|
|
||||||
node);
|
node);
|
||||||
|
|
||||||
// Push the flow generated by the {ib} split onto our list of
|
// 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!();
|
let mut consecutive_siblings = vec!();
|
||||||
|
|
||||||
inline_fragment_accumulator.fragments.push_all(initial_fragments);
|
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.
|
// List of absolute descendants, in tree order.
|
||||||
let mut abs_descendants = Descendants::new();
|
let mut abs_descendants = Descendants::new();
|
||||||
|
@ -640,8 +612,7 @@ impl<'a> FlowConstructor<'a> {
|
||||||
node,
|
node,
|
||||||
kid,
|
kid,
|
||||||
&mut inline_fragment_accumulator,
|
&mut inline_fragment_accumulator,
|
||||||
&mut abs_descendants,
|
&mut abs_descendants);
|
||||||
&mut first_fragment);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform a final flush of any inline fragments that we were gathering up to handle {ib}
|
// 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,
|
self.flush_inline_fragments_to_flow_or_list(inline_fragment_accumulator,
|
||||||
&mut flow,
|
&mut flow,
|
||||||
&mut consecutive_siblings,
|
&mut consecutive_siblings,
|
||||||
WhitespaceStrippingMode::FromEnd,
|
|
||||||
node);
|
node);
|
||||||
if !consecutive_siblings.is_empty() {
|
if !consecutive_siblings.is_empty() {
|
||||||
self.generate_anonymous_missing_child(consecutive_siblings, &mut flow, node);
|
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.
|
return // Fast path.
|
||||||
}
|
}
|
||||||
|
|
||||||
while !this.is_empty() && this.front().as_ref().unwrap().is_ignorable_whitespace() {
|
let mut leading_fragments_consisting_of_solely_bidi_control_characters = LinkedList::new();
|
||||||
debug!("stripping ignorable whitespace from start");
|
while !this.is_empty() {
|
||||||
drop(this.pop_front());
|
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.
|
/// 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
while !this.is_empty() && this.back().as_ref().unwrap().is_ignorable_whitespace() {
|
let mut trailing_fragments_consisting_of_solely_bidi_control_characters = LinkedList::new();
|
||||||
debug!("stripping ignorable whitespace from end");
|
while !this.is_empty() {
|
||||||
drop(this.pop_back());
|
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
|
/// If the 'unicode-bidi' property has a value other than 'normal', return the bidi control codes
|
||||||
|
|
|
@ -23,6 +23,7 @@ use euclid::{Point2D, Rect, Size2D};
|
||||||
use gfx::display_list::{BLUR_INFLATION_FACTOR, OpaqueNode};
|
use gfx::display_list::{BLUR_INFLATION_FACTOR, OpaqueNode};
|
||||||
use gfx::text::glyph::CharIndex;
|
use gfx::text::glyph::CharIndex;
|
||||||
use gfx::text::text_run::{TextRun, TextRunSlice};
|
use gfx::text::text_run::{TextRun, TextRunSlice};
|
||||||
|
use gfx;
|
||||||
use ipc_channel::ipc::IpcSender;
|
use ipc_channel::ipc::IpcSender;
|
||||||
use msg::constellation_msg::{ConstellationChan, Msg, PipelineId, SubpageId};
|
use msg::constellation_msg::{ConstellationChan, Msg, PipelineId, SubpageId};
|
||||||
use net_traits::image::base::Image;
|
use net_traits::image::base::Image;
|
||||||
|
@ -210,6 +211,9 @@ impl fmt::Debug for SpecificFragmentInfo {
|
||||||
write!(f, " \"{}\"", slice_chars(&*info.run.text, info.range.begin().get() as usize,
|
write!(f, " \"{}\"", slice_chars(&*info.run.text, info.range.begin().get() as usize,
|
||||||
info.range.end().get() as usize))
|
info.range.end().get() as usize))
|
||||||
}
|
}
|
||||||
|
SpecificFragmentInfo::UnscannedText(ref info) => {
|
||||||
|
write!(f, " \"{}\"", info.text)
|
||||||
|
}
|
||||||
_ => Ok(())
|
_ => Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2102,18 +2106,13 @@ impl Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn strip_leading_whitespace_if_necessary(&mut self) {
|
pub fn strip_leading_whitespace_if_necessary(&mut self) -> WhitespaceStrippingResult {
|
||||||
let mut scanned_text_fragment_info = match self.specific {
|
|
||||||
SpecificFragmentInfo::ScannedText(ref mut scanned_text_fragment_info) => {
|
|
||||||
scanned_text_fragment_info
|
|
||||||
}
|
|
||||||
_ => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
if self.style.get_inheritedtext().white_space == white_space::T::pre {
|
if self.style.get_inheritedtext().white_space == white_space::T::pre {
|
||||||
return
|
return WhitespaceStrippingResult::RetainFragment
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match self.specific {
|
||||||
|
SpecificFragmentInfo::ScannedText(ref mut scanned_text_fragment_info) => {
|
||||||
let mut leading_whitespace_character_count = 0;
|
let mut leading_whitespace_character_count = 0;
|
||||||
{
|
{
|
||||||
let text = slice_chars(
|
let text = slice_chars(
|
||||||
|
@ -2129,8 +2128,122 @@ impl Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scanned_text_fragment_info.range.adjust_by(CharIndex(leading_whitespace_character_count),
|
let whitespace_range = Range::new(scanned_text_fragment_info.range.begin(),
|
||||||
|
CharIndex(leading_whitespace_character_count));
|
||||||
|
let text_bounds =
|
||||||
|
scanned_text_fragment_info.run.metrics_for_range(&whitespace_range).bounding_box;
|
||||||
|
self.border_box.size.inline = self.border_box.size.inline - text_bounds.size.width;
|
||||||
|
scanned_text_fragment_info.content_size.inline =
|
||||||
|
scanned_text_fragment_info.content_size.inline - text_bounds.size.width;
|
||||||
|
|
||||||
|
scanned_text_fragment_info.range.adjust_by(
|
||||||
|
CharIndex(leading_whitespace_character_count),
|
||||||
-CharIndex(leading_whitespace_character_count));
|
-CharIndex(leading_whitespace_character_count));
|
||||||
|
|
||||||
|
return WhitespaceStrippingResult::RetainFragment
|
||||||
|
}
|
||||||
|
SpecificFragmentInfo::UnscannedText(ref mut unscanned_text_fragment_info) => {
|
||||||
|
let mut new_text_string = String::new();
|
||||||
|
let mut modified = false;
|
||||||
|
for (i, character) in unscanned_text_fragment_info.text.char_indices() {
|
||||||
|
if gfx::text::util::is_bidi_control(character) {
|
||||||
|
new_text_string.push(character);
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if util::str::char_is_whitespace(character) {
|
||||||
|
modified = true;
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
new_text_string.push_str(&unscanned_text_fragment_info.text[i..]);
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if modified {
|
||||||
|
unscanned_text_fragment_info.text = new_text_string.into_boxed_slice();
|
||||||
|
}
|
||||||
|
|
||||||
|
WhitespaceStrippingResult::from_unscanned_text_fragment_info(
|
||||||
|
&unscanned_text_fragment_info)
|
||||||
|
}
|
||||||
|
_ => WhitespaceStrippingResult::RetainFragment,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the entire fragment was stripped.
|
||||||
|
pub fn strip_trailing_whitespace_if_necessary(&mut self) -> WhitespaceStrippingResult {
|
||||||
|
if self.style.get_inheritedtext().white_space == white_space::T::pre {
|
||||||
|
return WhitespaceStrippingResult::RetainFragment
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.specific {
|
||||||
|
SpecificFragmentInfo::ScannedText(ref mut scanned_text_fragment_info) => {
|
||||||
|
// FIXME(pcwalton): Is there a more clever (i.e. faster) way to do this?
|
||||||
|
debug!("stripping trailing whitespace: range={:?}, len={}",
|
||||||
|
scanned_text_fragment_info.range,
|
||||||
|
scanned_text_fragment_info.run.text.chars().count());
|
||||||
|
let mut trailing_whitespace_character_count = 0;
|
||||||
|
let text_bounds;
|
||||||
|
{
|
||||||
|
let text = slice_chars(&*scanned_text_fragment_info.run.text,
|
||||||
|
scanned_text_fragment_info.range.begin().to_usize(),
|
||||||
|
scanned_text_fragment_info.range.end().to_usize());
|
||||||
|
for ch in text.chars().rev() {
|
||||||
|
if util::str::char_is_whitespace(ch) {
|
||||||
|
trailing_whitespace_character_count += 1
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let whitespace_range =
|
||||||
|
Range::new(scanned_text_fragment_info.range.end() -
|
||||||
|
CharIndex(trailing_whitespace_character_count),
|
||||||
|
CharIndex(trailing_whitespace_character_count));
|
||||||
|
text_bounds = scanned_text_fragment_info.run
|
||||||
|
.metrics_for_range(&whitespace_range)
|
||||||
|
.bounding_box;
|
||||||
|
self.border_box.size.inline = self.border_box.size.inline -
|
||||||
|
text_bounds.size.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
scanned_text_fragment_info.content_size.inline =
|
||||||
|
scanned_text_fragment_info.content_size.inline - text_bounds.size.width;
|
||||||
|
|
||||||
|
if trailing_whitespace_character_count != 0 {
|
||||||
|
scanned_text_fragment_info.range.extend_by(
|
||||||
|
CharIndex(-trailing_whitespace_character_count));
|
||||||
|
}
|
||||||
|
|
||||||
|
WhitespaceStrippingResult::RetainFragment
|
||||||
|
}
|
||||||
|
SpecificFragmentInfo::UnscannedText(ref mut unscanned_text_fragment_info) => {
|
||||||
|
let mut trailing_bidi_control_characters_to_retain = Vec::new();
|
||||||
|
let (mut modified, mut last_character_index) = (true, 0);
|
||||||
|
for (i, character) in unscanned_text_fragment_info.text.char_indices().rev() {
|
||||||
|
if gfx::text::util::is_bidi_control(character) {
|
||||||
|
trailing_bidi_control_characters_to_retain.push(character);
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if util::str::char_is_whitespace(character) {
|
||||||
|
modified = true;
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
last_character_index = i + character.len_utf8();
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if modified {
|
||||||
|
let mut text = unscanned_text_fragment_info.text.to_string();
|
||||||
|
text.truncate(last_character_index);
|
||||||
|
for character in trailing_bidi_control_characters_to_retain.iter().rev() {
|
||||||
|
text.push(*character);
|
||||||
|
}
|
||||||
|
unscanned_text_fragment_info.text = text.into_boxed_slice();
|
||||||
|
}
|
||||||
|
|
||||||
|
WhitespaceStrippingResult::from_unscanned_text_fragment_info(
|
||||||
|
&unscanned_text_fragment_info)
|
||||||
|
}
|
||||||
|
_ => WhitespaceStrippingResult::RetainFragment,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inline_styles<'a>(&'a self) -> InlineStyleIterator<'a> {
|
pub fn inline_styles<'a>(&'a self) -> InlineStyleIterator<'a> {
|
||||||
|
@ -2232,3 +2345,24 @@ impl<'a> InlineStyleIterator<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum WhitespaceStrippingResult {
|
||||||
|
RetainFragment,
|
||||||
|
FragmentContainedOnlyBidiControlCharacters,
|
||||||
|
FragmentContainedOnlyWhitespace,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WhitespaceStrippingResult {
|
||||||
|
fn from_unscanned_text_fragment_info(info: &UnscannedTextFragmentInfo)
|
||||||
|
-> WhitespaceStrippingResult {
|
||||||
|
if info.text.is_empty() {
|
||||||
|
WhitespaceStrippingResult::FragmentContainedOnlyWhitespace
|
||||||
|
} else if info.text.chars().all(gfx::text::util::is_bidi_control) {
|
||||||
|
WhitespaceStrippingResult::FragmentContainedOnlyBidiControlCharacters
|
||||||
|
} else {
|
||||||
|
WhitespaceStrippingResult::RetainFragment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,8 +21,6 @@ use euclid::{Point2D, Rect, Size2D};
|
||||||
use gfx::display_list::OpaqueNode;
|
use gfx::display_list::OpaqueNode;
|
||||||
use gfx::font::FontMetrics;
|
use gfx::font::FontMetrics;
|
||||||
use gfx::font_context::FontContext;
|
use gfx::font_context::FontContext;
|
||||||
use gfx::text::glyph::CharIndex;
|
|
||||||
use gfx::text::text_run::TextRun;
|
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
@ -36,7 +34,6 @@ use unicode_bidi;
|
||||||
use util::geometry::{Au, MAX_AU, ZERO_RECT};
|
use util::geometry::{Au, MAX_AU, ZERO_RECT};
|
||||||
use util::logical_geometry::{LogicalRect, LogicalSize, WritingMode};
|
use util::logical_geometry::{LogicalRect, LogicalSize, WritingMode};
|
||||||
use util::range::{Range, RangeIndex};
|
use util::range::{Range, RangeIndex};
|
||||||
use util::str::slice_chars;
|
|
||||||
use util;
|
use util;
|
||||||
|
|
||||||
// From gfxFontConstants.h in Firefox
|
// From gfxFontConstants.h in Firefox
|
||||||
|
@ -408,21 +405,27 @@ impl LineBreaker {
|
||||||
}
|
}
|
||||||
let last_fragment_index = self.pending_line.range.end() - FragmentIndex(1);
|
let last_fragment_index = self.pending_line.range.end() - FragmentIndex(1);
|
||||||
let mut fragment = &mut self.new_fragments[last_fragment_index.get() as usize];
|
let mut fragment = &mut self.new_fragments[last_fragment_index.get() as usize];
|
||||||
|
|
||||||
|
let mut old_fragment_inline_size = None;
|
||||||
|
if let SpecificFragmentInfo::ScannedText(_) = fragment.specific {
|
||||||
|
old_fragment_inline_size = Some(fragment.border_box.size.inline +
|
||||||
|
fragment.margin.inline_start_end());
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment.strip_trailing_whitespace_if_necessary();
|
||||||
|
|
||||||
if let SpecificFragmentInfo::ScannedText(ref mut scanned_text_fragment_info) =
|
if let SpecificFragmentInfo::ScannedText(ref mut scanned_text_fragment_info) =
|
||||||
fragment.specific {
|
fragment.specific {
|
||||||
let scanned_text_fragment_info = &mut **scanned_text_fragment_info;
|
let scanned_text_fragment_info = &mut **scanned_text_fragment_info;
|
||||||
let mut range = &mut scanned_text_fragment_info.range;
|
let range = &mut scanned_text_fragment_info.range;
|
||||||
strip_trailing_whitespace_if_necessary(&*scanned_text_fragment_info.run, range);
|
|
||||||
|
|
||||||
let old_fragment_inline_size = fragment.border_box.size.inline +
|
|
||||||
fragment.margin.inline_start_end();
|
|
||||||
scanned_text_fragment_info.content_size.inline =
|
scanned_text_fragment_info.content_size.inline =
|
||||||
scanned_text_fragment_info.run.metrics_for_range(range).advance_width;
|
scanned_text_fragment_info.run.metrics_for_range(range).advance_width;
|
||||||
fragment.border_box.size.inline = scanned_text_fragment_info.content_size.inline +
|
fragment.border_box.size.inline = scanned_text_fragment_info.content_size.inline +
|
||||||
fragment.border_padding.inline_start_end();
|
fragment.border_padding.inline_start_end();
|
||||||
self.pending_line.bounds.size.inline = self.pending_line.bounds.size.inline -
|
self.pending_line.bounds.size.inline = self.pending_line.bounds.size.inline -
|
||||||
(old_fragment_inline_size - (fragment.border_box.size.inline +
|
(old_fragment_inline_size.unwrap() -
|
||||||
fragment.margin.inline_start_end()))
|
(fragment.border_box.size.inline + fragment.margin.inline_start_end()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1869,24 +1872,3 @@ enum LineFlushMode {
|
||||||
Flush,
|
Flush,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given a range and a text run, adjusts the range to eliminate trailing whitespace.
|
|
||||||
fn strip_trailing_whitespace_if_necessary(text_run: &TextRun, range: &mut Range<CharIndex>) {
|
|
||||||
// FIXME(pcwalton): Is there a more clever (i.e. faster) way to do this?
|
|
||||||
debug!("stripping trailing whitespace: range={:?}, len={}",
|
|
||||||
range,
|
|
||||||
text_run.text.chars().count());
|
|
||||||
let text = slice_chars(&*text_run.text, range.begin().to_usize(), range.end().to_usize());
|
|
||||||
let mut trailing_whitespace_character_count = 0;
|
|
||||||
for ch in text.chars().rev() {
|
|
||||||
if util::str::char_is_whitespace(ch) {
|
|
||||||
trailing_whitespace_character_count += 1
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if trailing_whitespace_character_count != 0 {
|
|
||||||
range.extend_by(CharIndex(-trailing_whitespace_character_count));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -90,7 +90,7 @@ impl TextRunScanner {
|
||||||
// FIXME(pcwalton): We want to be sure not to allocate multiple times, since this is a
|
// FIXME(pcwalton): We want to be sure not to allocate multiple times, since this is a
|
||||||
// performance-critical spot, but this may overestimate and allocate too much memory.
|
// performance-critical spot, but this may overestimate and allocate too much memory.
|
||||||
let mut new_fragments = Vec::with_capacity(fragments.len());
|
let mut new_fragments = Vec::with_capacity(fragments.len());
|
||||||
let mut last_whitespace = true;
|
let mut last_whitespace = false;
|
||||||
let mut paragraph_bytes_processed = 0;
|
let mut paragraph_bytes_processed = 0;
|
||||||
|
|
||||||
while !fragments.is_empty() {
|
while !fragments.is_empty() {
|
||||||
|
@ -142,7 +142,7 @@ impl TextRunScanner {
|
||||||
debug_assert!(self.clump.len() == 1,
|
debug_assert!(self.clump.len() == 1,
|
||||||
"WAT: can't coalesce non-text nodes in flush_clump_to_list()!");
|
"WAT: can't coalesce non-text nodes in flush_clump_to_list()!");
|
||||||
out_fragments.push(self.clump.pop_front().unwrap());
|
out_fragments.push(self.clump.pop_front().unwrap());
|
||||||
return last_whitespace
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -398,14 +398,13 @@ fn split_first_fragment_at_newline_if_necessary(fragments: &mut LinkedList<Fragm
|
||||||
};
|
};
|
||||||
|
|
||||||
string_before =
|
string_before =
|
||||||
unscanned_text_fragment_info.text[..(position + 1)].to_owned().into_boxed_slice();
|
unscanned_text_fragment_info.text[..(position + 1)].to_owned();
|
||||||
unscanned_text_fragment_info.text =
|
unscanned_text_fragment_info.text =
|
||||||
unscanned_text_fragment_info.text[(position + 1)..].to_owned().into_boxed_slice();
|
unscanned_text_fragment_info.text[(position + 1)..].to_owned().into_boxed_slice();
|
||||||
}
|
}
|
||||||
first_fragment.transform(first_fragment.border_box.size,
|
first_fragment.transform(first_fragment.border_box.size,
|
||||||
SpecificFragmentInfo::UnscannedText(UnscannedTextFragmentInfo {
|
SpecificFragmentInfo::UnscannedText(
|
||||||
text: string_before,
|
UnscannedTextFragmentInfo::from_text(string_before)))
|
||||||
}))
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fragments.push_front(new_fragment);
|
fragments.push_front(new_fragment);
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
[absolute-replaced-height-036.htm]
|
||||||
|
type: reftest
|
||||||
|
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
||||||
[bidi-breaking-001.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[block-in-inline-007.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +1,4 @@
|
||||||
[c5502-imrgn-r-003.htm]
|
[c5502-imrgn-r-003.htm]
|
||||||
type: reftest
|
type: reftest
|
||||||
expected: FAIL
|
expected:
|
||||||
|
if os == "mac": FAIL
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[c5525-fltwidth-003.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[content-175.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[floats-143.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[font-weight-applies-to-017.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,4 +1,3 @@
|
||||||
[inline-formatting-context-007.htm]
|
[inline-formatting-context-007.htm]
|
||||||
type: reftest
|
type: reftest
|
||||||
expected:
|
expected: FAIL
|
||||||
if os == "mac": FAIL
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[insert-inline-in-blocks-n-inlines-begin-003.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[insert-inline-in-blocks-n-inlines-middle-003.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[ltr-basic.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,4 +0,0 @@
|
||||||
[margin-collapse-137.htm]
|
|
||||||
type: reftest
|
|
||||||
expected:
|
|
||||||
if os == "linux": FAIL
|
|
|
@ -1,4 +0,0 @@
|
||||||
[margin-collapse-138.htm]
|
|
||||||
type: reftest
|
|
||||||
expected:
|
|
||||||
if os == "linux": FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[max-width-percentage-001.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[min-width-percentage-001.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[rtl-basic.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[text-transform-capitalize-001.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[text-transform-lowercase-001.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[text-transform-uppercase-001.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +1,3 @@
|
||||||
[abspos-008.htm]
|
[vertical-align-sub-001.htm]
|
||||||
type: reftest
|
type: reftest
|
||||||
expected: FAIL
|
expected: FAIL
|
|
@ -1,3 +1,3 @@
|
||||||
[abspos-width-005.htm]
|
[vertical-align-super-001.htm]
|
||||||
type: reftest
|
type: reftest
|
||||||
expected: FAIL
|
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
||||||
[white-space-collapsing-bidi-003.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[white-space-normal-003.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[white-space-normal-004.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[white-space-pre-001.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[white-space-pre-002.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[width-percentage-001.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[width-percentage-002.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
Loading…
Add table
Add a link
Reference in a new issue