aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/layout/construct.rs20
-rw-r--r--components/layout/css/matching.rs11
-rw-r--r--components/layout/fragment.rs87
-rw-r--r--components/layout/inline.rs105
-rw-r--r--components/layout/text.rs6
-rw-r--r--components/util/lib.rs10
6 files changed, 209 insertions, 30 deletions
diff --git a/components/layout/construct.rs b/components/layout/construct.rs
index 9c99c9a495c..c795f5025f4 100644
--- a/components/layout/construct.rs
+++ b/components/layout/construct.rs
@@ -33,6 +33,7 @@ use fragment::{InlineAbsoluteHypotheticalFragmentInfo, InlineBlockFragment};
use fragment::{InlineBlockFragmentInfo, InputFragment, SpecificFragmentInfo, TableCellFragment};
use fragment::{TableColumnFragment, TableColumnFragmentInfo, TableFragment, TableRowFragment};
use fragment::{TableWrapperFragment, UnscannedTextFragment, UnscannedTextFragmentInfo};
+use incremental::RestyleDamage;
use inline::{InlineFragments, InlineFlow};
use parallel;
use table_wrapper::TableWrapperFlow;
@@ -103,7 +104,7 @@ pub enum ConstructionItem {
/// Inline fragments and associated {ib} splits that have not yet found flows.
InlineFragmentsConstructionItem(InlineFragmentsConstructionResult),
/// Potentially ignorable whitespace.
- WhitespaceConstructionItem(OpaqueNode, Arc<ComputedValues>),
+ WhitespaceConstructionItem(OpaqueNode, Arc<ComputedValues>, RestyleDamage),
/// TableColumn Fragment
TableColumnFragmentConstructionItem(Fragment),
}
@@ -295,13 +296,15 @@ impl<'a> FlowConstructor<'a> {
match whitespace_stripping {
NoWhitespaceStripping => {}
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() {
return
};
}
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() {
return
};
@@ -441,13 +444,15 @@ impl<'a> FlowConstructor<'a> {
abs_descendants.push_descendants(kid_abs_descendants);
}
ConstructionItemConstructionResult(WhitespaceConstructionItem(whitespace_node,
- whitespace_style)) => {
+ whitespace_style,
+ whitespace_damage)) => {
// Add whitespace results. They will be stripped out later on when
// between block elements, and retained when between inline elements.
let fragment_info =
UnscannedTextFragment(UnscannedTextFragmentInfo::from_text(" ".to_string()));
let mut fragment = Fragment::from_opaque_node_and_style(whitespace_node,
whitespace_style,
+ whitespace_damage,
fragment_info);
inline_fragment_accumulator.fragments.push(&mut fragment);
}
@@ -607,11 +612,13 @@ impl<'a> FlowConstructor<'a> {
abs_descendants.push_descendants(kid_abs_descendants);
}
ConstructionItemConstructionResult(WhitespaceConstructionItem(whitespace_node,
- whitespace_style)) => {
+ whitespace_style,
+ whitespace_damage)) => {
// Instantiate the whitespace fragment.
let fragment_info = UnscannedTextFragment(UnscannedTextFragmentInfo::from_text(" ".to_string()));
let mut fragment = Fragment::from_opaque_node_and_style(whitespace_node,
whitespace_style,
+ whitespace_damage,
fragment_info);
fragment_accumulator.fragments.push(&mut fragment)
}
@@ -653,7 +660,8 @@ impl<'a> FlowConstructor<'a> {
let opaque_node = OpaqueNodeMethods::from_thread_safe_layout_node(node);
return ConstructionItemConstructionResult(WhitespaceConstructionItem(
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
diff --git a/components/layout/css/matching.rs b/components/layout/css/matching.rs
index 7783c04aa0a..2c30e9d8fd2 100644
--- a/components/layout/css/matching.rs
+++ b/components/layout/css/matching.rs
@@ -13,6 +13,7 @@ use script::dom::node::{TextNodeTypeId};
use servo_util::bloom::BloomFilter;
use servo_util::cache::{Cache, LRUCache, SimpleHashCache};
use servo_util::smallvec::{SmallVec, SmallVec16};
+use servo_util::arc_ptr_eq;
use std::mem;
use std::hash::{Hash, sip};
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> {
fn equiv(&self, other: &ApplicableDeclarationsCacheEntry) -> bool {
if self.declarations.len() != other.declarations.len() {
diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs
index dd692eed75a..73afce62279 100644
--- a/components/layout/fragment.rs
+++ b/components/layout/fragment.rs
@@ -10,8 +10,10 @@ use css::node_style::StyledNode;
use construct::FlowConstructor;
use context::LayoutContext;
use floats::{ClearBoth, ClearLeft, ClearRight, ClearType};
+use flow;
use flow::Flow;
use flow_ref::FlowRef;
+use incremental::RestyleDamage;
use inline::{InlineFragmentContext, InlineMetrics};
use layout_debug;
use model::{Auto, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, Specified, specified};
@@ -88,6 +90,9 @@ pub struct Fragment {
/// The CSS style of this fragment.
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 size includes padding and border, but not margin.
pub border_box: LogicalRect<Au>,
@@ -148,6 +153,47 @@ pub enum SpecificFragmentInfo {
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
/// with `display: inline;`.
///
@@ -330,6 +376,10 @@ pub struct ScannedTextFragmentInfo {
/// The range within the above text run that this represents.
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 {
@@ -338,6 +388,7 @@ impl ScannedTextFragmentInfo {
ScannedTextFragmentInfo {
run: run,
range: range,
+ original_new_line_pos: None,
}
}
}
@@ -424,6 +475,7 @@ impl Fragment {
Fragment {
node: OpaqueNodeMethods::from_thread_safe_layout_node(node),
style: style,
+ restyle_damage: node.restyle_damage(),
border_box: LogicalRect::zero(writing_mode),
border_padding: LogicalMargin::zero(writing_mode),
margin: LogicalMargin::zero(writing_mode),
@@ -442,6 +494,7 @@ impl Fragment {
Fragment {
node: OpaqueNodeMethods::from_thread_safe_layout_node(node),
style: style,
+ restyle_damage: node.restyle_damage(),
border_box: LogicalRect::zero(writing_mode),
border_padding: LogicalMargin::zero(writing_mode),
margin: LogicalMargin::zero(writing_mode),
@@ -468,6 +521,7 @@ impl Fragment {
Fragment {
node: OpaqueNodeMethods::from_thread_safe_layout_node(node),
style: Arc::new(node_style),
+ restyle_damage: node.restyle_damage(),
border_box: LogicalRect::zero(writing_mode),
border_padding: LogicalMargin::zero(writing_mode),
margin: LogicalMargin::zero(writing_mode),
@@ -481,12 +535,14 @@ impl Fragment {
/// Constructs a new `Fragment` instance from an opaque node.
pub fn from_opaque_node_and_style(node: OpaqueNode,
style: Arc<ComputedValues>,
+ restyle_damage: RestyleDamage,
specific: SpecificFragmentInfo)
-> Fragment {
let writing_mode = style.writing_mode;
Fragment {
node: node,
style: style,
+ restyle_damage: restyle_damage,
border_box: LogicalRect::zero(writing_mode),
border_padding: 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
/// layouts or fragment manipulations.
pub fn debug_id(&self) -> uint {
@@ -509,6 +591,7 @@ impl Fragment {
Fragment {
node: self.node,
style: self.style.clone(),
+ restyle_damage: RestyleDamage::all(),
border_box: LogicalRect::from_point_size(
self.style.writing_mode, self.border_box.start, size),
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
/// context doesn't exist yet, it will be created.
pub fn add_inline_context_style(&mut self, style: Arc<ComputedValues>) {
diff --git a/components/layout/inline.rs b/components/layout/inline.rs
index 1e7e202689c..70b7ffa7d3e 100644
--- a/components/layout/inline.rs
+++ b/components/layout/inline.rs
@@ -11,6 +11,7 @@ use flow::{BaseFlow, FlowClass, Flow, InlineFlowClass, MutableFlowUtils};
use flow;
use fragment::{Fragment, InlineAbsoluteHypotheticalFragment, InlineBlockFragment};
use fragment::{ScannedTextFragment, ScannedTextFragmentInfo, SplitInfo};
+use incremental::RestyleDamage;
use layout_debug;
use model::IntrinsicISizesContribution;
use text;
@@ -25,6 +26,7 @@ use gfx::text::glyph::CharIndex;
use servo_util::geometry::Au;
use servo_util::logical_geometry::{LogicalRect, LogicalSize};
use servo_util::range::{IntRangeIndex, Range, RangeIndex};
+use servo_util::arc_ptr_eq;
use std::cmp::max;
use std::fmt;
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
/// 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.
-#[deriving(Encodable)]
+#[deriving(Encodable, Show)]
pub struct Line {
/// 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.
+ debug!("Scanning for lines. {} fragments.", old_fragments.len());
let mut old_fragment_iter = old_fragments.fragments.iter();
loop {
// 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 info = ScannedTextFragmentInfo::new(run.clone(), split.range);
let specific = ScannedTextFragment(info);
- let size = LogicalSize::new(writing_mode,
- split.inline_size,
- in_fragment.border_box.size.block);
+ let size = LogicalSize::new(
+ writing_mode, split.inline_size, in_fragment.border_box.size.block);
in_fragment.transform(size, specific)
};
debug!("LineBreaker: Pushing the fragment to the inline_start of the new-line character \
to the line.");
let mut inline_start = split_fragment(inline_start);
+ inline_start.save_new_line_pos();
inline_start.new_line_pos = vec![];
self.push_fragment_to_line(inline_start);
@@ -605,19 +608,21 @@ impl InlineFragments {
}
/// Strips ignorable whitespace from the start of a list of fragments.
- pub fn strip_ignorable_whitespace_from_start(&mut self) {
- // Fast path.
- if self.is_empty() {
- return
- }
+ ///
+ /// Returns some damage that must be added to the `InlineFlow`.
+ pub fn strip_ignorable_whitespace_from_start(&mut self) -> RestyleDamage {
+ if self.is_empty() { return RestyleDamage::empty() } // Fast path
// FIXME (rust#16151): This can be reverted back to using skip_while once
// the upstream bug is fixed.
let mut fragments = mem::replace(&mut self.fragments, vec![]).into_iter();
let mut new_fragments = Vec::new();
let mut skipping = true;
+ let mut damage = RestyleDamage::empty();
+
for fragment in fragments {
if skipping && fragment.is_ignorable_whitespace() {
+ damage = RestyleDamage::all();
debug!("stripping ignorable whitespace from start");
continue
}
@@ -627,24 +632,82 @@ impl InlineFragments {
}
self.fragments = new_fragments;
+ damage
}
/// 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() {
- return
+ return RestyleDamage::empty();
}
+ let mut damage = RestyleDamage::empty();
+
let mut new_fragments = self.fragments.clone();
while new_fragments.len() > 0 &&
new_fragments.as_slice().last().as_ref().unwrap().is_ignorable_whitespace() {
debug!("stripping ignorable whitespace from end");
+ damage = RestyleDamage::all();
drop(new_fragments.pop());
}
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)
}
+
+ 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 {
@@ -914,6 +987,8 @@ impl Flow for InlineFlow {
}
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 writing_mode = self.base.writing_mode;
@@ -989,6 +1064,12 @@ impl Flow for InlineFlow {
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 mut scanner = LineBreaker::new(scanner_floats);
scanner.scan_for_lines(self, layout_context);
diff --git a/components/layout/text.rs b/components/layout/text.rs
index 27bc60e39ab..e50eb74a91e 100644
--- a/components/layout/text.rs
+++ b/components/layout/text.rs
@@ -199,7 +199,10 @@ impl TextRunScanner {
// Next, concatenate all of the transformed strings together, saving the new
// character indices.
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);
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);
@@ -323,4 +326,3 @@ pub fn line_height_from_style(style: &ComputedValues, metrics: &FontMetrics) ->
line_height::Length(l) => l
}
}
-
diff --git a/components/util/lib.rs b/components/util/lib.rs
index 58289bfa581..e820c0e2c11 100644
--- a/components/util/lib.rs
+++ b/components/util/lib.rs
@@ -32,6 +32,8 @@ extern crate url;
#[phase(plugin)]
extern crate string_cache_macros;
+use std::sync::Arc;
+
pub mod bloom;
pub mod cache;
pub mod debug_utils;
@@ -55,3 +57,11 @@ pub mod workqueue;
pub fn 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)
+}