Skip to content
Permalink
Browse files

Preserve the un-truncated version of fragments

Fixes #14952
  • Loading branch information
notriddle committed Jan 12, 2017
1 parent 8a90cda commit cef4ebed2088ea5e4c457eba34fbd48f00fefaff
Showing with 154 additions and 32 deletions.
  1. +10 −5 components/layout/display_list_builder.rs
  2. +134 −12 components/layout/fragment.rs
  3. +10 −15 components/layout/inline.rs
@@ -19,7 +19,7 @@ use flex::FlexFlow;
use flow::{BaseFlow, Flow, IS_ABSOLUTELY_POSITIONED};
use flow_ref::FlowRef;
use fragment::{CoordinateSystem, Fragment, ImageFragmentInfo, ScannedTextFragmentInfo};
use fragment::SpecificFragmentInfo;
use fragment::{SpecificFragmentInfo, TruncatedFragmentInfo};
use gfx::display_list::{BLUR_INFLATION_FACTOR, BaseDisplayItem, BorderDisplayItem};
use gfx::display_list::{BorderRadii, BoxShadowClipMode, BoxShadowDisplayItem, ClippingRegion};
use gfx::display_list::{DisplayItem, DisplayItemMetadata, DisplayList, DisplayListSection};
@@ -1402,23 +1402,27 @@ impl FragmentDisplayListBuilding for Fragment {
self.stacking_relative_content_box(stacking_relative_border_box);

match self.specific {
SpecificFragmentInfo::ScannedText(ref text_fragment) => {
SpecificFragmentInfo::TruncatedFragment(box TruncatedFragmentInfo {
text_info: Some(ref text_fragment),
..
}) |
SpecificFragmentInfo::ScannedText(box ref text_fragment) => {
// Create items for shadows.
//
// NB: According to CSS-BACKGROUNDS, text shadows render in *reverse* order (front
// to back).

for text_shadow in self.style.get_inheritedtext().text_shadow.0.iter().rev() {
self.build_display_list_for_text_fragment(state,
&**text_fragment,
&*text_fragment,
&stacking_relative_content_box,
Some(text_shadow),
clip);
}

// Create the main text display item.
self.build_display_list_for_text_fragment(state,
&**text_fragment,
&*text_fragment,
&stacking_relative_content_box,
None,
clip);
@@ -1428,7 +1432,7 @@ impl FragmentDisplayListBuilding for Fragment {
self.style(),
stacking_relative_border_box,
&stacking_relative_content_box,
&**text_fragment,
&*text_fragment,
clip);
}
}
@@ -1443,6 +1447,7 @@ impl FragmentDisplayListBuilding for Fragment {
SpecificFragmentInfo::InlineBlock(_) |
SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
SpecificFragmentInfo::InlineAbsolute(_) |
SpecificFragmentInfo::TruncatedFragment(_) |
SpecificFragmentInfo::Svg(_) => {
if opts::get().show_debug_fragment_borders {
self.build_debug_borders_around_fragment(state,
@@ -186,6 +186,12 @@ pub enum SpecificFragmentInfo {
Multicol,
MulticolColumn,
UnscannedText(Box<UnscannedTextFragmentInfo>),

/// A container for a fragment that got truncated by text-overflow.
/// "Totally truncated fragments" are not rendered at all.
/// Text fragments may be partially truncated (in which case this renders like a text fragment).
/// Other fragments can only be totally truncated or not truncated at all.
TruncatedFragment(Box<TruncatedFragmentInfo>),
}

impl SpecificFragmentInfo {
@@ -206,6 +212,7 @@ impl SpecificFragmentInfo {
SpecificFragmentInfo::Multicol |
SpecificFragmentInfo::MulticolColumn |
SpecificFragmentInfo::UnscannedText(_) |
SpecificFragmentInfo::TruncatedFragment(_) |
SpecificFragmentInfo::Generic => return RestyleDamage::empty(),
SpecificFragmentInfo::InlineAbsoluteHypothetical(ref info) => &info.flow_ref,
SpecificFragmentInfo::InlineAbsolute(ref info) => &info.flow_ref,
@@ -237,6 +244,7 @@ impl SpecificFragmentInfo {
SpecificFragmentInfo::Multicol => "SpecificFragmentInfo::Multicol",
SpecificFragmentInfo::MulticolColumn => "SpecificFragmentInfo::MulticolColumn",
SpecificFragmentInfo::UnscannedText(_) => "SpecificFragmentInfo::UnscannedText",
SpecificFragmentInfo::TruncatedFragment(_) => "SpecificFragmentInfo::TruncatedFragment"
}
}
}
@@ -573,11 +581,11 @@ pub struct SplitResult {
}

/// Describes how a fragment should be truncated.
pub struct TruncationResult {
struct TruncationResult {
/// The part of the fragment remaining after truncation.
pub split: SplitInfo,
split: SplitInfo,
/// The text run which is being truncated.
pub text_run: Arc<TextRun>,
text_run: Arc<TextRun>,
}

/// Data for an unscanned text fragment. Unscanned text fragments are the results of flow
@@ -622,6 +630,15 @@ impl TableColumnFragmentInfo {
}
}

/// A wrapper for fragments that have been truncated by the `text-overflow` property.
/// This may have an associated text node, or, if the fragment was completely truncated,
/// it may act as an invisible marker for incremental reflow.
#[derive(Clone)]
pub struct TruncatedFragmentInfo {
pub text_info: Option<ScannedTextFragmentInfo>,
pub full: Fragment,
}

impl Fragment {
/// Constructs a new `Fragment` instance.
pub fn new<N: ThreadSafeLayoutNode>(node: &N, specific: SpecificFragmentInfo, ctx: &LayoutContext) -> Fragment {
@@ -846,6 +863,7 @@ impl Fragment {
base_quantities
}
}
SpecificFragmentInfo::TruncatedFragment(_) |
SpecificFragmentInfo::ScannedText(_) |
SpecificFragmentInfo::TableColumn(_) |
SpecificFragmentInfo::UnscannedText(_) |
@@ -1501,7 +1519,11 @@ impl Fragment {
});
}

SpecificFragmentInfo::ScannedText(ref text_fragment_info) => {
SpecificFragmentInfo::TruncatedFragment(box TruncatedFragmentInfo {
text_info: Some(ref text_fragment_info),
..
}) |
SpecificFragmentInfo::ScannedText(box ref text_fragment_info) => {
let range = &text_fragment_info.range;

// See http://dev.w3.org/csswg/css-sizing/#max-content-inline-size.
@@ -1521,6 +1543,12 @@ impl Fragment {
preferred_inline_size: max_line_inline_size,
})
}

SpecificFragmentInfo::TruncatedFragment(box TruncatedFragmentInfo {
text_info: None,
..
}) => return IntrinsicISizesContribution::new(),

SpecificFragmentInfo::UnscannedText(..) => {
panic!("Unscanned text fragments should have been scanned by now!")
}
@@ -1562,7 +1590,11 @@ impl Fragment {
/// this fragment.)
pub fn minimum_splittable_inline_size(&self) -> Au {
match self.specific {
SpecificFragmentInfo::ScannedText(ref text) => {
SpecificFragmentInfo::TruncatedFragment(box TruncatedFragmentInfo {
text_info: Some(ref text),
..
}) |
SpecificFragmentInfo::ScannedText(box ref text) => {
text.run.minimum_splittable_inline_size(&text.range)
}
_ => Au(0),
@@ -1623,9 +1655,55 @@ impl Fragment {
}
}

/// Truncates this fragment to the given `max_inline_size`, using a character-based breaking
/// strategy. The resulting fragment will have `SpecificFragmentInfo::TruncatedFragment`,
/// preserving the original fragment for use in incremental reflow.
///
/// This function will panic if self is already truncated.
pub fn truncate_to_inline_size(self, max_inline_size: Au) -> Fragment {
if let SpecificFragmentInfo::TruncatedFragment(_) = self.specific {
panic!("Cannot truncate an already truncated fragment");
}
let info = self.calculate_truncate_to_inline_size(max_inline_size);
let (size, text_info) = match info {
Some(TruncationResult { split: SplitInfo { inline_size, range }, text_run } ) => {
let size = LogicalSize::new(self.style.writing_mode,
inline_size,
self.border_box.size.block);
// Preserve the insertion point if it is in this fragment's range or it is at line end.
let (flags, insertion_point) = match self.specific {
SpecificFragmentInfo::ScannedText(ref info) => {
match info.insertion_point {
Some(index) if range.contains(index) => (info.flags, info.insertion_point),
Some(index) if index == ByteIndex(text_run.text.chars().count() as isize - 1) &&
index == range.end() => (info.flags, info.insertion_point),
_ => (info.flags, None)
}
},
_ => (ScannedTextFlags::empty(), None)
};
let text_info = ScannedTextFragmentInfo::new(
text_run,
range,
size,
insertion_point,
flags);
(size, Some(text_info))
}
None =>
(LogicalSize::zero(self.style.writing_mode), None)
};
let mut result = self.transform(size, SpecificFragmentInfo::Generic);
result.specific = SpecificFragmentInfo::TruncatedFragment(box TruncatedFragmentInfo {
text_info: text_info,
full: self,
});
result
}

/// Truncates this fragment to the given `max_inline_size`, using a character-based breaking
/// strategy. If no characters could fit, returns `None`.
pub fn truncate_to_inline_size(&self, max_inline_size: Au) -> Option<TruncationResult> {
fn calculate_truncate_to_inline_size(&self, max_inline_size: Au) -> Option<TruncationResult> {
let text_fragment_info =
if let SpecificFragmentInfo::ScannedText(ref text_fragment_info) = self.specific {
text_fragment_info
@@ -1821,6 +1899,10 @@ impl Fragment {
container_inline_size: Au,
container_block_size: Option<Au>) {
match self.specific {
SpecificFragmentInfo::TruncatedFragment(box TruncatedFragmentInfo {
text_info: None,
..
}) |
SpecificFragmentInfo::Generic |
SpecificFragmentInfo::GeneratedContent(_) |
SpecificFragmentInfo::Table |
@@ -1842,6 +1924,7 @@ impl Fragment {
SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
SpecificFragmentInfo::InlineAbsolute(_) |
SpecificFragmentInfo::ScannedText(_) |
SpecificFragmentInfo::TruncatedFragment(_) |
SpecificFragmentInfo::Svg(_) => {}
};

@@ -1873,7 +1956,11 @@ impl Fragment {
}

// Text
SpecificFragmentInfo::ScannedText(ref info) => {
SpecificFragmentInfo::TruncatedFragment(box TruncatedFragmentInfo {
text_info: Some(ref info),
..
}) |
SpecificFragmentInfo::ScannedText(box ref info) => {
// Scanned text fragments will have already had their content inline-sizes assigned
// by this point.
self.border_box.size.inline = info.content_size.inline +
@@ -1898,6 +1985,10 @@ impl Fragment {
/// Ideally, this should follow CSS 2.1 § 10.6.2.
pub fn assign_replaced_block_size_if_necessary(&mut self) {
match self.specific {
SpecificFragmentInfo::TruncatedFragment(box TruncatedFragmentInfo {
text_info: None,
..
}) |
SpecificFragmentInfo::Generic |
SpecificFragmentInfo::GeneratedContent(_) |
SpecificFragmentInfo::Table |
@@ -1919,12 +2010,20 @@ impl Fragment {
SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
SpecificFragmentInfo::InlineAbsolute(_) |
SpecificFragmentInfo::ScannedText(_) |
SpecificFragmentInfo::TruncatedFragment(box TruncatedFragmentInfo {
text_info: Some(_),
..
}) |
SpecificFragmentInfo::Svg(_) => {}
}

match self.specific {
// Text
SpecificFragmentInfo::ScannedText(ref info) => {
SpecificFragmentInfo::TruncatedFragment(box TruncatedFragmentInfo {
text_info: Some(ref info),
..
}) |
SpecificFragmentInfo::ScannedText(box ref info) => {
// Scanned text fragments' content block-sizes are calculated by the text run
// scanner during flow construction.
self.border_box.size.block = info.content_size.block +
@@ -2001,7 +2100,11 @@ impl Fragment {
ascent: ascent,
}
}
SpecificFragmentInfo::ScannedText(ref info) => {
SpecificFragmentInfo::TruncatedFragment(box TruncatedFragmentInfo {
text_info: Some(ref info),
..
}) |
SpecificFragmentInfo::ScannedText(box ref info) => {
// Fragments with no glyphs don't contribute any inline metrics.
// TODO: Filter out these fragments during flow construction?
if info.insertion_point.is_none() && info.content_size.inline == Au(0) {
@@ -2019,6 +2122,10 @@ impl Fragment {
SpecificFragmentInfo::InlineAbsoluteHypothetical(ref info) => {
inline_metrics_of_block(&info.flow_ref, &*self.style)
}
SpecificFragmentInfo::TruncatedFragment(box TruncatedFragmentInfo {
text_info: None,
..
}) |
SpecificFragmentInfo::InlineAbsolute(_) => {
InlineMetrics::new(Au(0), Au(0), Au(0))
}
@@ -2267,6 +2374,7 @@ impl Fragment {
SpecificFragmentInfo::TableCell |
SpecificFragmentInfo::TableColumn(_) |
SpecificFragmentInfo::TableRow |
SpecificFragmentInfo::TruncatedFragment(_) |
SpecificFragmentInfo::Multicol |
SpecificFragmentInfo::UnscannedText(_) => true,
}
@@ -2354,6 +2462,7 @@ impl Fragment {
pub fn establishes_stacking_context(&self) -> bool {
// Text fragments shouldn't create stacking contexts.
match self.specific {
SpecificFragmentInfo::TruncatedFragment(_) |
SpecificFragmentInfo::ScannedText(_) |
SpecificFragmentInfo::UnscannedText(_) => return false,
_ => {}
@@ -2484,7 +2593,11 @@ impl Fragment {

pub fn requires_line_break_afterward_if_wrapping_on_newlines(&self) -> bool {
match self.specific {
SpecificFragmentInfo::ScannedText(ref scanned_text) => {
SpecificFragmentInfo::TruncatedFragment(box TruncatedFragmentInfo {
text_info: Some(ref scanned_text),
..
}) |
SpecificFragmentInfo::ScannedText(box ref scanned_text) => {
scanned_text.requires_line_break_afterward_if_wrapping_on_newlines()
}
_ => false,
@@ -2497,7 +2610,11 @@ impl Fragment {
}

match self.specific {
SpecificFragmentInfo::ScannedText(ref mut scanned_text_fragment_info) => {
SpecificFragmentInfo::TruncatedFragment(box TruncatedFragmentInfo {
text_info: Some(ref mut scanned_text_fragment_info),
..
}) |
SpecificFragmentInfo::ScannedText(box ref mut scanned_text_fragment_info) => {
let leading_whitespace_byte_count = scanned_text_fragment_info.text()
.find(|c| !char_is_whitespace(c))
.unwrap_or(scanned_text_fragment_info.text().len());
@@ -2551,7 +2668,11 @@ impl Fragment {
}

match self.specific {
SpecificFragmentInfo::ScannedText(ref mut scanned_text_fragment_info) => {
SpecificFragmentInfo::TruncatedFragment(box TruncatedFragmentInfo {
text_info: Some(ref mut scanned_text_fragment_info),
..
}) |
SpecificFragmentInfo::ScannedText(box ref mut scanned_text_fragment_info) => {
let mut trailing_whitespace_start_byte = 0;
for (i, c) in scanned_text_fragment_info.text().char_indices().rev() {
if !char_is_whitespace(c) {
@@ -2737,6 +2858,7 @@ impl Fragment {
SpecificFragmentInfo::Iframe(_) |
SpecificFragmentInfo::Image(_) |
SpecificFragmentInfo::ScannedText(_) |
SpecificFragmentInfo::TruncatedFragment(_) |
SpecificFragmentInfo::Svg(_) |
SpecificFragmentInfo::UnscannedText(_) => true
}

0 comments on commit cef4ebe

Please sign in to comment.
You can’t perform that action at this time.