mirror of
https://github.com/servo/servo.git
synced 2025-07-24 15:50:21 +01:00
Fix whitespace_pre with incremental reflow turned on.
This implements fragment merging, in order to incrementally reflow linebroken text. This makes the `whitespace_pre.html` reftest pass with incremental reflow turned on with `-i`.
This commit is contained in:
parent
6a11ee89de
commit
481adcd654
6 changed files with 209 additions and 30 deletions
|
@ -33,6 +33,7 @@ use fragment::{InlineAbsoluteHypotheticalFragmentInfo, InlineBlockFragment};
|
||||||
use fragment::{InlineBlockFragmentInfo, InputFragment, SpecificFragmentInfo, TableCellFragment};
|
use fragment::{InlineBlockFragmentInfo, InputFragment, SpecificFragmentInfo, TableCellFragment};
|
||||||
use fragment::{TableColumnFragment, TableColumnFragmentInfo, TableFragment, TableRowFragment};
|
use fragment::{TableColumnFragment, TableColumnFragmentInfo, TableFragment, TableRowFragment};
|
||||||
use fragment::{TableWrapperFragment, UnscannedTextFragment, UnscannedTextFragmentInfo};
|
use fragment::{TableWrapperFragment, UnscannedTextFragment, UnscannedTextFragmentInfo};
|
||||||
|
use incremental::RestyleDamage;
|
||||||
use inline::{InlineFragments, InlineFlow};
|
use inline::{InlineFragments, InlineFlow};
|
||||||
use parallel;
|
use parallel;
|
||||||
use table_wrapper::TableWrapperFlow;
|
use table_wrapper::TableWrapperFlow;
|
||||||
|
@ -103,7 +104,7 @@ pub enum ConstructionItem {
|
||||||
/// Inline fragments and associated {ib} splits that have not yet found flows.
|
/// Inline fragments and associated {ib} splits that have not yet found flows.
|
||||||
InlineFragmentsConstructionItem(InlineFragmentsConstructionResult),
|
InlineFragmentsConstructionItem(InlineFragmentsConstructionResult),
|
||||||
/// Potentially ignorable whitespace.
|
/// Potentially ignorable whitespace.
|
||||||
WhitespaceConstructionItem(OpaqueNode, Arc<ComputedValues>),
|
WhitespaceConstructionItem(OpaqueNode, Arc<ComputedValues>, RestyleDamage),
|
||||||
/// TableColumn Fragment
|
/// TableColumn Fragment
|
||||||
TableColumnFragmentConstructionItem(Fragment),
|
TableColumnFragmentConstructionItem(Fragment),
|
||||||
}
|
}
|
||||||
|
@ -295,13 +296,15 @@ impl<'a> FlowConstructor<'a> {
|
||||||
match whitespace_stripping {
|
match whitespace_stripping {
|
||||||
NoWhitespaceStripping => {}
|
NoWhitespaceStripping => {}
|
||||||
StripWhitespaceFromStart => {
|
StripWhitespaceFromStart => {
|
||||||
fragments.strip_ignorable_whitespace_from_start();
|
flow::mut_base(flow.deref_mut()).restyle_damage.insert(
|
||||||
|
fragments.strip_ignorable_whitespace_from_start());
|
||||||
if fragments.is_empty() {
|
if fragments.is_empty() {
|
||||||
return
|
return
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
StripWhitespaceFromEnd => {
|
StripWhitespaceFromEnd => {
|
||||||
fragments.strip_ignorable_whitespace_from_end();
|
flow::mut_base(flow.deref_mut()).restyle_damage.insert(
|
||||||
|
fragments.strip_ignorable_whitespace_from_end());
|
||||||
if fragments.is_empty() {
|
if fragments.is_empty() {
|
||||||
return
|
return
|
||||||
};
|
};
|
||||||
|
@ -441,13 +444,15 @@ impl<'a> FlowConstructor<'a> {
|
||||||
abs_descendants.push_descendants(kid_abs_descendants);
|
abs_descendants.push_descendants(kid_abs_descendants);
|
||||||
}
|
}
|
||||||
ConstructionItemConstructionResult(WhitespaceConstructionItem(whitespace_node,
|
ConstructionItemConstructionResult(WhitespaceConstructionItem(whitespace_node,
|
||||||
whitespace_style)) => {
|
whitespace_style,
|
||||||
|
whitespace_damage)) => {
|
||||||
// Add whitespace results. They will be stripped out later on when
|
// Add whitespace results. They will be stripped out later on when
|
||||||
// between block elements, and retained when between inline elements.
|
// between block elements, and retained when between inline elements.
|
||||||
let fragment_info =
|
let fragment_info =
|
||||||
UnscannedTextFragment(UnscannedTextFragmentInfo::from_text(" ".to_string()));
|
UnscannedTextFragment(UnscannedTextFragmentInfo::from_text(" ".to_string()));
|
||||||
let mut fragment = Fragment::from_opaque_node_and_style(whitespace_node,
|
let mut fragment = Fragment::from_opaque_node_and_style(whitespace_node,
|
||||||
whitespace_style,
|
whitespace_style,
|
||||||
|
whitespace_damage,
|
||||||
fragment_info);
|
fragment_info);
|
||||||
inline_fragment_accumulator.fragments.push(&mut fragment);
|
inline_fragment_accumulator.fragments.push(&mut fragment);
|
||||||
}
|
}
|
||||||
|
@ -607,11 +612,13 @@ impl<'a> FlowConstructor<'a> {
|
||||||
abs_descendants.push_descendants(kid_abs_descendants);
|
abs_descendants.push_descendants(kid_abs_descendants);
|
||||||
}
|
}
|
||||||
ConstructionItemConstructionResult(WhitespaceConstructionItem(whitespace_node,
|
ConstructionItemConstructionResult(WhitespaceConstructionItem(whitespace_node,
|
||||||
whitespace_style)) => {
|
whitespace_style,
|
||||||
|
whitespace_damage)) => {
|
||||||
// Instantiate the whitespace fragment.
|
// Instantiate the whitespace fragment.
|
||||||
let fragment_info = UnscannedTextFragment(UnscannedTextFragmentInfo::from_text(" ".to_string()));
|
let fragment_info = UnscannedTextFragment(UnscannedTextFragmentInfo::from_text(" ".to_string()));
|
||||||
let mut fragment = Fragment::from_opaque_node_and_style(whitespace_node,
|
let mut fragment = Fragment::from_opaque_node_and_style(whitespace_node,
|
||||||
whitespace_style,
|
whitespace_style,
|
||||||
|
whitespace_damage,
|
||||||
fragment_info);
|
fragment_info);
|
||||||
fragment_accumulator.fragments.push(&mut fragment)
|
fragment_accumulator.fragments.push(&mut fragment)
|
||||||
}
|
}
|
||||||
|
@ -653,7 +660,8 @@ impl<'a> FlowConstructor<'a> {
|
||||||
let opaque_node = OpaqueNodeMethods::from_thread_safe_layout_node(node);
|
let opaque_node = OpaqueNodeMethods::from_thread_safe_layout_node(node);
|
||||||
return ConstructionItemConstructionResult(WhitespaceConstructionItem(
|
return ConstructionItemConstructionResult(WhitespaceConstructionItem(
|
||||||
opaque_node,
|
opaque_node,
|
||||||
node.style().clone()))
|
node.style().clone(),
|
||||||
|
node.restyle_damage()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is generated content, then we need to initialize the accumulator with the
|
// If this is generated content, then we need to initialize the accumulator with the
|
||||||
|
|
|
@ -13,6 +13,7 @@ use script::dom::node::{TextNodeTypeId};
|
||||||
use servo_util::bloom::BloomFilter;
|
use servo_util::bloom::BloomFilter;
|
||||||
use servo_util::cache::{Cache, LRUCache, SimpleHashCache};
|
use servo_util::cache::{Cache, LRUCache, SimpleHashCache};
|
||||||
use servo_util::smallvec::{SmallVec, SmallVec16};
|
use servo_util::smallvec::{SmallVec, SmallVec16};
|
||||||
|
use servo_util::arc_ptr_eq;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::hash::{Hash, sip};
|
use std::hash::{Hash, sip};
|
||||||
use std::slice::Items;
|
use std::slice::Items;
|
||||||
|
@ -91,16 +92,6 @@ impl<'a> ApplicableDeclarationsCacheQuery<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Workaround for lack of `ptr_eq` on Arcs...
|
|
||||||
#[inline]
|
|
||||||
fn arc_ptr_eq<T>(a: &Arc<T>, b: &Arc<T>) -> bool {
|
|
||||||
unsafe {
|
|
||||||
let a: uint = mem::transmute_copy(a);
|
|
||||||
let b: uint = mem::transmute_copy(b);
|
|
||||||
a == b
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Equiv<ApplicableDeclarationsCacheEntry> for ApplicableDeclarationsCacheQuery<'a> {
|
impl<'a> Equiv<ApplicableDeclarationsCacheEntry> for ApplicableDeclarationsCacheQuery<'a> {
|
||||||
fn equiv(&self, other: &ApplicableDeclarationsCacheEntry) -> bool {
|
fn equiv(&self, other: &ApplicableDeclarationsCacheEntry) -> bool {
|
||||||
if self.declarations.len() != other.declarations.len() {
|
if self.declarations.len() != other.declarations.len() {
|
||||||
|
|
|
@ -10,8 +10,10 @@ use css::node_style::StyledNode;
|
||||||
use construct::FlowConstructor;
|
use construct::FlowConstructor;
|
||||||
use context::LayoutContext;
|
use context::LayoutContext;
|
||||||
use floats::{ClearBoth, ClearLeft, ClearRight, ClearType};
|
use floats::{ClearBoth, ClearLeft, ClearRight, ClearType};
|
||||||
|
use flow;
|
||||||
use flow::Flow;
|
use flow::Flow;
|
||||||
use flow_ref::FlowRef;
|
use flow_ref::FlowRef;
|
||||||
|
use incremental::RestyleDamage;
|
||||||
use inline::{InlineFragmentContext, InlineMetrics};
|
use inline::{InlineFragmentContext, InlineMetrics};
|
||||||
use layout_debug;
|
use layout_debug;
|
||||||
use model::{Auto, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, Specified, specified};
|
use model::{Auto, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, Specified, specified};
|
||||||
|
@ -88,6 +90,9 @@ pub struct Fragment {
|
||||||
/// The CSS style of this fragment.
|
/// The CSS style of this fragment.
|
||||||
pub style: Arc<ComputedValues>,
|
pub style: Arc<ComputedValues>,
|
||||||
|
|
||||||
|
/// How damaged this fragment is since last reflow.
|
||||||
|
pub restyle_damage: RestyleDamage,
|
||||||
|
|
||||||
/// The position of this fragment relative to its owning flow.
|
/// The position of this fragment relative to its owning flow.
|
||||||
/// The size includes padding and border, but not margin.
|
/// The size includes padding and border, but not margin.
|
||||||
pub border_box: LogicalRect<Au>,
|
pub border_box: LogicalRect<Au>,
|
||||||
|
@ -148,6 +153,47 @@ pub enum SpecificFragmentInfo {
|
||||||
UnscannedTextFragment(UnscannedTextFragmentInfo),
|
UnscannedTextFragment(UnscannedTextFragmentInfo),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SpecificFragmentInfo {
|
||||||
|
fn restyle_damage(&self) -> RestyleDamage {
|
||||||
|
let flow =
|
||||||
|
match *self {
|
||||||
|
IframeFragment(_)
|
||||||
|
| ImageFragment(_)
|
||||||
|
| InputFragment
|
||||||
|
| ScannedTextFragment(_)
|
||||||
|
| TableFragment
|
||||||
|
| TableCellFragment
|
||||||
|
| TableColumnFragment(_)
|
||||||
|
| TableRowFragment
|
||||||
|
| TableWrapperFragment
|
||||||
|
| UnscannedTextFragment(_)
|
||||||
|
| GenericFragment => return RestyleDamage::empty(),
|
||||||
|
InlineAbsoluteHypotheticalFragment(ref info) => &info.flow_ref,
|
||||||
|
InlineBlockFragment(ref info) => &info.flow_ref,
|
||||||
|
};
|
||||||
|
|
||||||
|
flow::base(flow.deref()).restyle_damage
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_type(&self) -> &'static str {
|
||||||
|
match *self {
|
||||||
|
GenericFragment => "GenericFragment",
|
||||||
|
IframeFragment(_) => "IframeFragment",
|
||||||
|
ImageFragment(_) => "ImageFragment",
|
||||||
|
InlineAbsoluteHypotheticalFragment(_) => "InlineAbsoluteHypotheticalFragment",
|
||||||
|
InlineBlockFragment(_) => "InlineBlockFragment",
|
||||||
|
InputFragment => "InputFragment",
|
||||||
|
ScannedTextFragment(_) => "ScannedTextFragment",
|
||||||
|
TableFragment => "TableFragment",
|
||||||
|
TableCellFragment => "TableCellFragment",
|
||||||
|
TableColumnFragment(_) => "TableColumnFragment",
|
||||||
|
TableRowFragment => "TableRowFragment",
|
||||||
|
TableWrapperFragment => "TableWrapperFragment",
|
||||||
|
UnscannedTextFragment(_) => "UnscannedTextFragment",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A hypothetical box (see CSS 2.1 § 10.3.7) for an absolutely-positioned block that was declared
|
/// A hypothetical box (see CSS 2.1 § 10.3.7) for an absolutely-positioned block that was declared
|
||||||
/// with `display: inline;`.
|
/// with `display: inline;`.
|
||||||
///
|
///
|
||||||
|
@ -330,6 +376,10 @@ pub struct ScannedTextFragmentInfo {
|
||||||
|
|
||||||
/// The range within the above text run that this represents.
|
/// The range within the above text run that this represents.
|
||||||
pub range: Range<CharIndex>,
|
pub range: Range<CharIndex>,
|
||||||
|
|
||||||
|
/// The new_line_pos is eaten during line breaking. If we need to re-merge
|
||||||
|
/// fragments, it will have to be restored.
|
||||||
|
pub original_new_line_pos: Option<Vec<CharIndex>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScannedTextFragmentInfo {
|
impl ScannedTextFragmentInfo {
|
||||||
|
@ -338,6 +388,7 @@ impl ScannedTextFragmentInfo {
|
||||||
ScannedTextFragmentInfo {
|
ScannedTextFragmentInfo {
|
||||||
run: run,
|
run: run,
|
||||||
range: range,
|
range: range,
|
||||||
|
original_new_line_pos: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -424,6 +475,7 @@ impl Fragment {
|
||||||
Fragment {
|
Fragment {
|
||||||
node: OpaqueNodeMethods::from_thread_safe_layout_node(node),
|
node: OpaqueNodeMethods::from_thread_safe_layout_node(node),
|
||||||
style: style,
|
style: style,
|
||||||
|
restyle_damage: node.restyle_damage(),
|
||||||
border_box: LogicalRect::zero(writing_mode),
|
border_box: LogicalRect::zero(writing_mode),
|
||||||
border_padding: LogicalMargin::zero(writing_mode),
|
border_padding: LogicalMargin::zero(writing_mode),
|
||||||
margin: LogicalMargin::zero(writing_mode),
|
margin: LogicalMargin::zero(writing_mode),
|
||||||
|
@ -442,6 +494,7 @@ impl Fragment {
|
||||||
Fragment {
|
Fragment {
|
||||||
node: OpaqueNodeMethods::from_thread_safe_layout_node(node),
|
node: OpaqueNodeMethods::from_thread_safe_layout_node(node),
|
||||||
style: style,
|
style: style,
|
||||||
|
restyle_damage: node.restyle_damage(),
|
||||||
border_box: LogicalRect::zero(writing_mode),
|
border_box: LogicalRect::zero(writing_mode),
|
||||||
border_padding: LogicalMargin::zero(writing_mode),
|
border_padding: LogicalMargin::zero(writing_mode),
|
||||||
margin: LogicalMargin::zero(writing_mode),
|
margin: LogicalMargin::zero(writing_mode),
|
||||||
|
@ -468,6 +521,7 @@ impl Fragment {
|
||||||
Fragment {
|
Fragment {
|
||||||
node: OpaqueNodeMethods::from_thread_safe_layout_node(node),
|
node: OpaqueNodeMethods::from_thread_safe_layout_node(node),
|
||||||
style: Arc::new(node_style),
|
style: Arc::new(node_style),
|
||||||
|
restyle_damage: node.restyle_damage(),
|
||||||
border_box: LogicalRect::zero(writing_mode),
|
border_box: LogicalRect::zero(writing_mode),
|
||||||
border_padding: LogicalMargin::zero(writing_mode),
|
border_padding: LogicalMargin::zero(writing_mode),
|
||||||
margin: LogicalMargin::zero(writing_mode),
|
margin: LogicalMargin::zero(writing_mode),
|
||||||
|
@ -481,12 +535,14 @@ impl Fragment {
|
||||||
/// Constructs a new `Fragment` instance from an opaque node.
|
/// Constructs a new `Fragment` instance from an opaque node.
|
||||||
pub fn from_opaque_node_and_style(node: OpaqueNode,
|
pub fn from_opaque_node_and_style(node: OpaqueNode,
|
||||||
style: Arc<ComputedValues>,
|
style: Arc<ComputedValues>,
|
||||||
|
restyle_damage: RestyleDamage,
|
||||||
specific: SpecificFragmentInfo)
|
specific: SpecificFragmentInfo)
|
||||||
-> Fragment {
|
-> Fragment {
|
||||||
let writing_mode = style.writing_mode;
|
let writing_mode = style.writing_mode;
|
||||||
Fragment {
|
Fragment {
|
||||||
node: node,
|
node: node,
|
||||||
style: style,
|
style: style,
|
||||||
|
restyle_damage: restyle_damage,
|
||||||
border_box: LogicalRect::zero(writing_mode),
|
border_box: LogicalRect::zero(writing_mode),
|
||||||
border_padding: LogicalMargin::zero(writing_mode),
|
border_padding: LogicalMargin::zero(writing_mode),
|
||||||
margin: LogicalMargin::zero(writing_mode),
|
margin: LogicalMargin::zero(writing_mode),
|
||||||
|
@ -497,6 +553,32 @@ impl Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Saves the new_line_pos vector into a `ScannedTextFragment`. This will fail
|
||||||
|
/// if called on any other type of fragment.
|
||||||
|
pub fn save_new_line_pos(&mut self) {
|
||||||
|
match &mut self.specific {
|
||||||
|
&ScannedTextFragment(ref mut info) => {
|
||||||
|
if !self.new_line_pos.is_empty() {
|
||||||
|
info.original_new_line_pos = Some(self.new_line_pos.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn restore_new_line_pos(&mut self) {
|
||||||
|
match &mut self.specific {
|
||||||
|
&ScannedTextFragment(ref mut info) => {
|
||||||
|
match info.original_new_line_pos.take() {
|
||||||
|
None => {}
|
||||||
|
Some(new_line_pos) => self.new_line_pos = new_line_pos,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a debug ID of this fragment. This ID should not be considered stable across multiple
|
/// Returns a debug ID of this fragment. This ID should not be considered stable across multiple
|
||||||
/// layouts or fragment manipulations.
|
/// layouts or fragment manipulations.
|
||||||
pub fn debug_id(&self) -> uint {
|
pub fn debug_id(&self) -> uint {
|
||||||
|
@ -509,6 +591,7 @@ impl Fragment {
|
||||||
Fragment {
|
Fragment {
|
||||||
node: self.node,
|
node: self.node,
|
||||||
style: self.style.clone(),
|
style: self.style.clone(),
|
||||||
|
restyle_damage: RestyleDamage::all(),
|
||||||
border_box: LogicalRect::from_point_size(
|
border_box: LogicalRect::from_point_size(
|
||||||
self.style.writing_mode, self.border_box.start, size),
|
self.style.writing_mode, self.border_box.start, size),
|
||||||
border_padding: self.border_padding,
|
border_padding: self.border_padding,
|
||||||
|
@ -520,6 +603,10 @@ impl Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn restyle_damage(&self) -> RestyleDamage {
|
||||||
|
self.restyle_damage | self.specific.restyle_damage()
|
||||||
|
}
|
||||||
|
|
||||||
/// Adds a style to the inline context for this fragment. If the inline
|
/// Adds a style to the inline context for this fragment. If the inline
|
||||||
/// context doesn't exist yet, it will be created.
|
/// context doesn't exist yet, it will be created.
|
||||||
pub fn add_inline_context_style(&mut self, style: Arc<ComputedValues>) {
|
pub fn add_inline_context_style(&mut self, style: Arc<ComputedValues>) {
|
||||||
|
|
|
@ -11,6 +11,7 @@ use flow::{BaseFlow, FlowClass, Flow, InlineFlowClass, MutableFlowUtils};
|
||||||
use flow;
|
use flow;
|
||||||
use fragment::{Fragment, InlineAbsoluteHypotheticalFragment, InlineBlockFragment};
|
use fragment::{Fragment, InlineAbsoluteHypotheticalFragment, InlineBlockFragment};
|
||||||
use fragment::{ScannedTextFragment, ScannedTextFragmentInfo, SplitInfo};
|
use fragment::{ScannedTextFragment, ScannedTextFragmentInfo, SplitInfo};
|
||||||
|
use incremental::RestyleDamage;
|
||||||
use layout_debug;
|
use layout_debug;
|
||||||
use model::IntrinsicISizesContribution;
|
use model::IntrinsicISizesContribution;
|
||||||
use text;
|
use text;
|
||||||
|
@ -25,6 +26,7 @@ use gfx::text::glyph::CharIndex;
|
||||||
use servo_util::geometry::Au;
|
use servo_util::geometry::Au;
|
||||||
use servo_util::logical_geometry::{LogicalRect, LogicalSize};
|
use servo_util::logical_geometry::{LogicalRect, LogicalSize};
|
||||||
use servo_util::range::{IntRangeIndex, Range, RangeIndex};
|
use servo_util::range::{IntRangeIndex, Range, RangeIndex};
|
||||||
|
use servo_util::arc_ptr_eq;
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
@ -63,7 +65,7 @@ static FONT_SUPERSCRIPT_OFFSET_RATIO: f64 = 0.34;
|
||||||
/// with a float or a horizontal wall of the containing block. The block-start
|
/// with a float or a horizontal wall of the containing block. The block-start
|
||||||
/// inline-start corner of the green zone is the same as that of the line, but
|
/// inline-start corner of the green zone is the same as that of the line, but
|
||||||
/// the green zone can be taller and wider than the line itself.
|
/// the green zone can be taller and wider than the line itself.
|
||||||
#[deriving(Encodable)]
|
#[deriving(Encodable, Show)]
|
||||||
pub struct Line {
|
pub struct Line {
|
||||||
/// A range of line indices that describe line breaks.
|
/// A range of line indices that describe line breaks.
|
||||||
///
|
///
|
||||||
|
@ -217,6 +219,7 @@ impl LineBreaker {
|
||||||
|
|
||||||
{
|
{
|
||||||
// Enter a new scope so that `old_fragment_iter`'s borrow is released.
|
// 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();
|
let mut old_fragment_iter = old_fragments.fragments.iter();
|
||||||
loop {
|
loop {
|
||||||
// acquire the next fragment to lay out from work list or fragment list
|
// acquire the next fragment to lay out from work list or fragment list
|
||||||
|
@ -404,15 +407,15 @@ impl LineBreaker {
|
||||||
let split_fragment = |split: SplitInfo| {
|
let split_fragment = |split: SplitInfo| {
|
||||||
let info = ScannedTextFragmentInfo::new(run.clone(), split.range);
|
let info = ScannedTextFragmentInfo::new(run.clone(), split.range);
|
||||||
let specific = ScannedTextFragment(info);
|
let specific = ScannedTextFragment(info);
|
||||||
let size = LogicalSize::new(writing_mode,
|
let size = LogicalSize::new(
|
||||||
split.inline_size,
|
writing_mode, split.inline_size, in_fragment.border_box.size.block);
|
||||||
in_fragment.border_box.size.block);
|
|
||||||
in_fragment.transform(size, specific)
|
in_fragment.transform(size, specific)
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("LineBreaker: Pushing the fragment to the inline_start of the new-line character \
|
debug!("LineBreaker: Pushing the fragment to the inline_start of the new-line character \
|
||||||
to the line.");
|
to the line.");
|
||||||
let mut inline_start = split_fragment(inline_start);
|
let mut inline_start = split_fragment(inline_start);
|
||||||
|
inline_start.save_new_line_pos();
|
||||||
inline_start.new_line_pos = vec![];
|
inline_start.new_line_pos = vec![];
|
||||||
self.push_fragment_to_line(inline_start);
|
self.push_fragment_to_line(inline_start);
|
||||||
|
|
||||||
|
@ -605,19 +608,21 @@ impl InlineFragments {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Strips ignorable whitespace from the start of a list of fragments.
|
/// Strips ignorable whitespace from the start of a list of fragments.
|
||||||
pub fn strip_ignorable_whitespace_from_start(&mut self) {
|
///
|
||||||
// Fast path.
|
/// Returns some damage that must be added to the `InlineFlow`.
|
||||||
if self.is_empty() {
|
pub fn strip_ignorable_whitespace_from_start(&mut self) -> RestyleDamage {
|
||||||
return
|
if self.is_empty() { return RestyleDamage::empty() } // Fast path
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME (rust#16151): This can be reverted back to using skip_while once
|
// FIXME (rust#16151): This can be reverted back to using skip_while once
|
||||||
// the upstream bug is fixed.
|
// the upstream bug is fixed.
|
||||||
let mut fragments = mem::replace(&mut self.fragments, vec![]).into_iter();
|
let mut fragments = mem::replace(&mut self.fragments, vec![]).into_iter();
|
||||||
let mut new_fragments = Vec::new();
|
let mut new_fragments = Vec::new();
|
||||||
let mut skipping = true;
|
let mut skipping = true;
|
||||||
|
let mut damage = RestyleDamage::empty();
|
||||||
|
|
||||||
for fragment in fragments {
|
for fragment in fragments {
|
||||||
if skipping && fragment.is_ignorable_whitespace() {
|
if skipping && fragment.is_ignorable_whitespace() {
|
||||||
|
damage = RestyleDamage::all();
|
||||||
debug!("stripping ignorable whitespace from start");
|
debug!("stripping ignorable whitespace from start");
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -627,24 +632,82 @@ impl InlineFragments {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.fragments = new_fragments;
|
self.fragments = new_fragments;
|
||||||
|
damage
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Strips ignorable whitespace from the end of a list of fragments.
|
/// Strips ignorable whitespace from the end of a list of fragments.
|
||||||
pub fn strip_ignorable_whitespace_from_end(&mut self) {
|
///
|
||||||
// Fast path.
|
/// Returns some damage that must be added to the `InlineFlow`.
|
||||||
|
pub fn strip_ignorable_whitespace_from_end(&mut self) -> RestyleDamage {
|
||||||
if self.is_empty() {
|
if self.is_empty() {
|
||||||
return
|
return RestyleDamage::empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut damage = RestyleDamage::empty();
|
||||||
|
|
||||||
let mut new_fragments = self.fragments.clone();
|
let mut new_fragments = self.fragments.clone();
|
||||||
while new_fragments.len() > 0 &&
|
while new_fragments.len() > 0 &&
|
||||||
new_fragments.as_slice().last().as_ref().unwrap().is_ignorable_whitespace() {
|
new_fragments.as_slice().last().as_ref().unwrap().is_ignorable_whitespace() {
|
||||||
debug!("stripping ignorable whitespace from end");
|
debug!("stripping ignorable whitespace from end");
|
||||||
|
damage = RestyleDamage::all();
|
||||||
drop(new_fragments.pop());
|
drop(new_fragments.pop());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
self.fragments = new_fragments;
|
self.fragments = new_fragments;
|
||||||
|
damage
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function merges previously-line-broken fragments back into their
|
||||||
|
/// original, pre-line-breaking form.
|
||||||
|
pub fn merge_broken_lines(&mut self) {
|
||||||
|
let mut work: RingBuf<Fragment> =
|
||||||
|
mem::replace(&mut self.fragments, Vec::new()).into_iter().collect();
|
||||||
|
|
||||||
|
let mut out: Vec<Fragment> = Vec::new();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut left: Fragment =
|
||||||
|
match work.pop_front() {
|
||||||
|
None => break,
|
||||||
|
Some(work) => work,
|
||||||
|
};
|
||||||
|
|
||||||
|
let right: Fragment =
|
||||||
|
match work.pop_front() {
|
||||||
|
None => {
|
||||||
|
out.push(left);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Some(work) => work,
|
||||||
|
};
|
||||||
|
|
||||||
|
left.restore_new_line_pos();
|
||||||
|
|
||||||
|
let right_is_from_same_fragment =
|
||||||
|
match (&mut left.specific, &right.specific) {
|
||||||
|
(&ScannedTextFragment(ref mut left_info),
|
||||||
|
&ScannedTextFragment(ref right_info)) => {
|
||||||
|
if arc_ptr_eq(&left_info.run, &right_info.run)
|
||||||
|
&& left_info.range.end() + CharIndex(1) == right_info.range.begin() {
|
||||||
|
left_info.range.extend_by(right_info.range.length() + CharIndex(1));
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if right_is_from_same_fragment {
|
||||||
|
work.push_front(left);
|
||||||
|
} else {
|
||||||
|
out.push(left);
|
||||||
|
work.push_front(right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mem::replace(&mut self.fragments, out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -898,6 +961,16 @@ impl InlineFlow {
|
||||||
|
|
||||||
(block_size_above_baseline, depth_below_baseline)
|
(block_size_above_baseline, depth_below_baseline)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_restyle_damage(&mut self) {
|
||||||
|
let mut damage = self.base.restyle_damage;
|
||||||
|
|
||||||
|
for frag in self.fragments.fragments.iter() {
|
||||||
|
damage.insert(frag.restyle_damage());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.base.restyle_damage = damage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Flow for InlineFlow {
|
impl Flow for InlineFlow {
|
||||||
|
@ -914,6 +987,8 @@ impl Flow for InlineFlow {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bubble_inline_sizes(&mut self) {
|
fn bubble_inline_sizes(&mut self) {
|
||||||
|
self.update_restyle_damage();
|
||||||
|
|
||||||
let _scope = layout_debug_scope!("inline::bubble_inline_sizes {:s}", self.base.debug_id());
|
let _scope = layout_debug_scope!("inline::bubble_inline_sizes {:s}", self.base.debug_id());
|
||||||
|
|
||||||
let writing_mode = self.base.writing_mode;
|
let writing_mode = self.base.writing_mode;
|
||||||
|
@ -989,6 +1064,12 @@ impl Flow for InlineFlow {
|
||||||
containing_block_block_size);
|
containing_block_block_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug!("lines: {}", self.lines);
|
||||||
|
|
||||||
|
self.fragments.merge_broken_lines();
|
||||||
|
|
||||||
|
self.lines = Vec::new();
|
||||||
|
|
||||||
let scanner_floats = self.base.floats.clone();
|
let scanner_floats = self.base.floats.clone();
|
||||||
let mut scanner = LineBreaker::new(scanner_floats);
|
let mut scanner = LineBreaker::new(scanner_floats);
|
||||||
scanner.scan_for_lines(self, layout_context);
|
scanner.scan_for_lines(self, layout_context);
|
||||||
|
|
|
@ -199,7 +199,10 @@ impl TextRunScanner {
|
||||||
// Next, concatenate all of the transformed strings together, saving the new
|
// Next, concatenate all of the transformed strings together, saving the new
|
||||||
// character indices.
|
// character indices.
|
||||||
let mut run_str = String::new();
|
let mut run_str = String::new();
|
||||||
let mut new_ranges: Vec<Range<CharIndex>> = vec![];
|
|
||||||
|
let mut new_ranges: Vec<Range<CharIndex>> =
|
||||||
|
Vec::with_capacity(transformed_strs.len());
|
||||||
|
|
||||||
let mut char_total = CharIndex(0);
|
let mut char_total = CharIndex(0);
|
||||||
for i in range(0, transformed_strs.len() as int) {
|
for i in range(0, transformed_strs.len() as int) {
|
||||||
let added_chars = CharIndex(transformed_strs[i as uint].as_slice().char_len() as int);
|
let added_chars = CharIndex(transformed_strs[i as uint].as_slice().char_len() as int);
|
||||||
|
@ -323,4 +326,3 @@ pub fn line_height_from_style(style: &ComputedValues, metrics: &FontMetrics) ->
|
||||||
line_height::Length(l) => l
|
line_height::Length(l) => l
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,8 @@ extern crate url;
|
||||||
#[phase(plugin)]
|
#[phase(plugin)]
|
||||||
extern crate string_cache_macros;
|
extern crate string_cache_macros;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub mod bloom;
|
pub mod bloom;
|
||||||
pub mod cache;
|
pub mod cache;
|
||||||
pub mod debug_utils;
|
pub mod debug_utils;
|
||||||
|
@ -55,3 +57,11 @@ pub mod workqueue;
|
||||||
pub fn breakpoint() {
|
pub fn breakpoint() {
|
||||||
unsafe { ::std::intrinsics::breakpoint() };
|
unsafe { ::std::intrinsics::breakpoint() };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Workaround for lack of `ptr_eq` on Arcs...
|
||||||
|
#[inline]
|
||||||
|
pub fn arc_ptr_eq<T: 'static + Send + Sync>(a: &Arc<T>, b: &Arc<T>) -> bool {
|
||||||
|
let a: &T = a.deref();
|
||||||
|
let b: &T = b.deref();
|
||||||
|
(a as *const T) == (b as *const T)
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue