diff options
-rw-r--r-- | components/layout/construct.rs | 20 | ||||
-rw-r--r-- | components/layout/css/matching.rs | 11 | ||||
-rw-r--r-- | components/layout/fragment.rs | 87 | ||||
-rw-r--r-- | components/layout/inline.rs | 105 | ||||
-rw-r--r-- | components/layout/text.rs | 6 | ||||
-rw-r--r-- | components/util/lib.rs | 10 |
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) +} |