diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs index a72f0b44d08..7f8975a2ca6 100644 --- a/components/gfx/display_list/mod.rs +++ b/components/gfx/display_list/mod.rs @@ -288,10 +288,10 @@ impl DisplayList { pub fn draw_into_context(&self, render_context: &mut RenderContext, current_transform: &Matrix2D, - current_clip_rect: &Rect) { + current_clip_stack: &mut Vec>) { debug!("Beginning display list."); for item in self.list.iter() { - item.draw_into_context(render_context, current_transform, current_clip_rect) + item.draw_into_context(render_context, current_transform, current_clip_stack) } debug!("Ending display list."); } @@ -504,14 +504,19 @@ impl DisplayItem { fn draw_into_context(&self, render_context: &mut RenderContext, current_transform: &Matrix2D, - current_clip_rect: &Rect) { + current_clip_stack: &mut Vec>) { // This should have been flattened to the content stacking level first. assert!(self.base().level == ContentStackingLevel); + // TODO(pcwalton): This will need some tweaking to deal with more complex clipping regions. let clip_rect = &self.base().clip_rect; - let need_to_clip = current_clip_rect != clip_rect; - if need_to_clip { + if current_clip_stack.len() == 0 || current_clip_stack.last().unwrap() != clip_rect { + while current_clip_stack.len() != 0 { + render_context.draw_pop_clip(); + drop(current_clip_stack.pop()); + } render_context.draw_push_clip(clip_rect); + current_clip_stack.push(*clip_rect); } match *self { @@ -608,10 +613,6 @@ impl DisplayItem { PseudoDisplayItemClass(_) => {} } - - if need_to_clip { - render_context.draw_pop_clip(); - } } pub fn base<'a>(&'a self) -> &'a BaseDisplayItem { diff --git a/components/gfx/font.rs b/components/gfx/font.rs index fe9445be107..f88d964b831 100644 --- a/components/gfx/font.rs +++ b/components/gfx/font.rs @@ -100,14 +100,19 @@ pub struct Font { } impl Font { - pub fn shape_text(&mut self, text: String, is_whitespace: bool) -> Arc { + pub fn shape_text(&mut self, text: &str, is_whitespace: bool) -> Arc { self.make_shaper(); let shaper = &self.shaper; - self.shape_cache.find_or_create(&text, |txt| { - let mut glyphs = GlyphStore::new(text.as_slice().char_len() as int, is_whitespace); - shaper.as_ref().unwrap().shape_text(txt.as_slice(), &mut glyphs); - Arc::new(glyphs) - }) + match self.shape_cache.find_equiv(&text) { + None => {} + Some(glyphs) => return (*glyphs).clone(), + } + + let mut glyphs = GlyphStore::new(text.char_len() as int, is_whitespace); + shaper.as_ref().unwrap().shape_text(text, &mut glyphs); + let glyphs = Arc::new(glyphs); + self.shape_cache.insert(text.to_string(), glyphs.clone()); + glyphs } fn make_shaper<'a>(&'a mut self) -> &'a Shaper { diff --git a/components/gfx/font_context.rs b/components/gfx/font_context.rs index efb40d16abd..adcd9810b88 100644 --- a/components/gfx/font_context.rs +++ b/components/gfx/font_context.rs @@ -43,7 +43,7 @@ static SMALL_CAPS_SCALE_FACTOR: f64 = 0.8; // Matches FireFox (see gfxFont. struct LayoutFontCacheEntry { family: String, - font: Rc>, + font: Option>>, } struct FallbackFontCacheEntry { @@ -132,13 +132,21 @@ impl FontContext { let mut cache_hit = false; for cached_font_entry in self.layout_font_cache.iter() { if cached_font_entry.family.as_slice() == family.name() { - let cached_font = cached_font_entry.font.borrow(); - if cached_font.descriptor == desc && - cached_font.requested_pt_size == style.font_size.to_subpx() && - cached_font.variant == style.font_variant { - fonts.push(cached_font_entry.font.clone()); - cache_hit = true; - break; + match cached_font_entry.font { + None => { + cache_hit = true; + break; + } + Some(ref cached_font_ref) => { + let cached_font = cached_font_ref.borrow(); + if cached_font.descriptor == desc && + cached_font.requested_pt_size == style.font_size.to_subpx() && + cached_font.variant == style.font_variant { + fonts.push((*cached_font_ref).clone()); + cache_hit = true; + break; + } + } } } } @@ -156,11 +164,16 @@ impl FontContext { let layout_font = Rc::new(RefCell::new(layout_font)); self.layout_font_cache.push(LayoutFontCacheEntry { family: family.name().to_string(), - font: layout_font.clone(), + font: Some(layout_font.clone()), }); fonts.push(layout_font); } - None => {} + None => { + self.layout_font_cache.push(LayoutFontCacheEntry { + family: family.name().to_string(), + font: None, + }); + } } } } diff --git a/components/gfx/render_task.rs b/components/gfx/render_task.rs index 53f50f95102..32c5d97c2b1 100644 --- a/components/gfx/render_task.rs +++ b/components/gfx/render_task.rs @@ -25,14 +25,13 @@ use servo_msg::compositor_msg::{LayerMetadata, RenderListener, RenderingRenderSt use servo_msg::constellation_msg::{ConstellationChan, Failure, FailureMsg, PipelineId}; use servo_msg::constellation_msg::{RendererReadyMsg}; use servo_msg::platform::surface::NativeSurfaceAzureMethods; -use servo_util::geometry::{Au, mod}; +use servo_util::geometry; use servo_util::opts; use servo_util::smallvec::{SmallVec, SmallVec1}; use servo_util::task::spawn_named_with_send_on_failure; use servo_util::time::{TimeProfilerChan, profile}; use servo_util::time; use std::comm::{Receiver, Sender, channel}; -use std::i32; use sync::Arc; use font_cache_task::FontCacheTask; @@ -359,9 +358,8 @@ impl RenderTask { None, self.time_profiler_chan.clone(), || { - let clip_rect = Rect(Point2D(Au(i32::MIN), Au(i32::MIN)), - Size2D(Au(i32::MAX), Au(i32::MAX))); - display_list.draw_into_context(&mut ctx, &matrix, &clip_rect); + let mut clip_stack = Vec::new(); + display_list.draw_into_context(&mut ctx, &matrix, &mut clip_stack); ctx.draw_target.flush(); }); } diff --git a/components/gfx/text/text_run.rs b/components/gfx/text/text_run.rs index cdf7e5c9ddf..4ab5aed94e0 100644 --- a/components/gfx/text/text_run.rs +++ b/components/gfx/text/text_run.rs @@ -162,7 +162,7 @@ impl<'a> TextRun { // Create a glyph store for this slice if it's nonempty. if can_break_before && byte_i > byte_last_boundary { - let slice = text.slice(byte_last_boundary, byte_i).to_string(); + let slice = text.slice(byte_last_boundary, byte_i); debug!("creating glyph store for slice {} (ws? {}), {} - {} in run {}", slice, !cur_slice_is_whitespace, byte_last_boundary, byte_i, text); glyphs.push(GlyphRun { @@ -179,7 +179,7 @@ impl<'a> TextRun { // Create a glyph store for the final slice if it's nonempty. if byte_i > byte_last_boundary { - let slice = text.slice_from(byte_last_boundary).to_string(); + let slice = text.slice_from(byte_last_boundary); debug!("creating glyph store for final slice {} (ws? {}), {} - {} in run {}", slice, cur_slice_is_whitespace, byte_last_boundary, text.len(), text); glyphs.push(GlyphRun { diff --git a/components/gfx/text/util.rs b/components/gfx/text/util.rs index 23b8b029144..3c43b5d9b0f 100644 --- a/components/gfx/text/util.rs +++ b/components/gfx/text/util.rs @@ -17,15 +17,17 @@ pub enum CompressionMode { // High level TODOs: // // * Issue #113: consider incoming text state (arabic, etc) -// and propogate outgoing text state (dual of above) +// and propagate outgoing text state (dual of above) // // * Issue #114: record skipped and kept chars for mapping original to new text // // * Untracked: various edge cases for bidi, CJK, etc. -pub fn transform_text(text: &str, mode: CompressionMode, +pub fn transform_text(text: &str, + mode: CompressionMode, incoming_whitespace: bool, - new_line_pos: &mut Vec) -> (String, bool) { - let mut out_str = String::new(); + output_text: &mut String, + new_line_pos: &mut Vec) + -> bool { let out_whitespace = match mode { CompressNone | DiscardNewline => { let mut new_line_index = CharIndex(0); @@ -46,7 +48,7 @@ pub fn transform_text(text: &str, mode: CompressionMode, if ch != '\n' { new_line_index = new_line_index + CharIndex(1); } - out_str.push_char(ch); + output_text.push_char(ch); } } text.len() > 0 && is_in_whitespace(text.char_at_reverse(0), mode) @@ -65,14 +67,14 @@ pub fn transform_text(text: &str, mode: CompressionMode, // TODO: record skipped char } else { // TODO: record kept char - out_str.push_char(ch); + output_text.push_char(ch); } } else { /* next_in_whitespace; possibly add a space char */ if in_whitespace { // TODO: record skipped char } else { // TODO: record kept char - out_str.push_char(' '); + output_text.push_char(' '); } } // save whitespace context for next char @@ -82,7 +84,7 @@ pub fn transform_text(text: &str, mode: CompressionMode, } }; - return (out_str, out_whitespace); + return out_whitespace; fn is_in_whitespace(ch: char, mode: CompressionMode) -> bool { match (ch, mode) { @@ -155,7 +157,8 @@ fn test_transform_compress_none() { for test in test_strs.iter() { let mut new_line_pos = vec!(); - let (trimmed_str, _out) = transform_text(*test, mode, true, &mut new_line_pos); + let mut trimmed_str = String::new(); + transform_text(*test, mode, true, &mut trimmed_str, &mut new_line_pos); assert_eq!(trimmed_str.as_slice(), *test) } } @@ -187,7 +190,8 @@ fn test_transform_discard_newline() { for (test, oracle) in test_strs.iter().zip(oracle_strs.iter()) { let mut new_line_pos = vec!(); - let (trimmed_str, _out) = transform_text(*test, mode, true, &mut new_line_pos); + let mut trimmed_str = String::new(); + transform_text(*test, mode, true, &mut trimmed_str, &mut new_line_pos); assert_eq!(trimmed_str.as_slice(), *oracle) } } @@ -279,7 +283,8 @@ fn test_transform_compress_whitespace_newline_no_incoming() { for (test, oracle) in test_strs.iter().zip(oracle_strs.iter()) { let mut new_line_pos = vec!(); - let (trimmed_str, _out) = transform_text(*test, mode, false, &mut new_line_pos); + let mut trimmed_str = String::new(); + transform_text(*test, mode, false, &mut trimmed_str, &mut new_line_pos); assert_eq!(trimmed_str.as_slice(), *oracle) } } diff --git a/components/layout/construct.rs b/components/layout/construct.rs index c69d04d1d49..ca97d7e36b7 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -28,7 +28,7 @@ use fragment::{InlineBlockFragmentInfo, InputFragment, SpecificFragmentInfo, Tab use fragment::{TableColumnFragment, TableColumnFragmentInfo, TableFragment, TableRowFragment}; use fragment::{TableWrapperFragment, UnscannedTextFragment, UnscannedTextFragmentInfo}; use incremental::RestyleDamage; -use inline::{InlineFragments, InlineFlow}; +use inline::InlineFlow; use parallel; use table_wrapper::TableWrapperFlow; use table::TableFlow; @@ -53,6 +53,7 @@ use script::dom::node::{DocumentNodeTypeId, ElementNodeTypeId, ProcessingInstruc use script::dom::node::{TextNodeTypeId}; use script::dom::htmlobjectelement::is_image_data; use servo_util::opts; +use std::collections::{DList, Deque}; use std::mem; use std::sync::atomics::Relaxed; use style::ComputedValues; @@ -115,7 +116,7 @@ pub struct InlineFragmentsConstructionResult { pub splits: Vec, /// Any fragments that succeed the {ib} splits. - pub fragments: InlineFragments, + pub fragments: DList, /// Any absolute descendants that we're bubbling up. pub abs_descendants: AbsDescendants, @@ -150,7 +151,7 @@ pub struct InlineFragmentsConstructionResult { #[deriving(Clone)] pub struct InlineBlockSplit { /// The inline fragments that precede the flow. - pub predecessors: InlineFragments, + pub predecessors: DList, /// The flow that caused this {ib} split. pub flow: FlowRef, @@ -159,7 +160,7 @@ pub struct InlineBlockSplit { /// Holds inline fragments that we're gathering for children of an inline node. struct InlineFragmentsAccumulator { /// The list of fragments. - fragments: InlineFragments, + fragments: DList, /// Whether we've created a range to enclose all the fragments. This will be Some() if the outer node /// is an inline and None otherwise. @@ -169,20 +170,28 @@ struct InlineFragmentsAccumulator { impl InlineFragmentsAccumulator { fn new() -> InlineFragmentsAccumulator { InlineFragmentsAccumulator { - fragments: InlineFragments::new(), + fragments: DList::new(), enclosing_style: None, } } fn from_inline_node(node: &ThreadSafeLayoutNode) -> InlineFragmentsAccumulator { - let fragments = InlineFragments::new(); + let fragments = DList::new(); InlineFragmentsAccumulator { fragments: fragments, enclosing_style: Some(node.style().clone()), } } - fn finish(self) -> InlineFragments { + fn push_all(&mut self, fragments: DList) { + if fragments.len() == 0 { + return + } + + self.fragments.append(fragments) + } + + fn to_dlist(self) -> DList { let InlineFragmentsAccumulator { fragments: mut fragments, enclosing_style @@ -190,7 +199,7 @@ impl InlineFragmentsAccumulator { match enclosing_style { Some(enclosing_style) => { - for frag in fragments.fragments.iter_mut() { + for frag in fragments.iter_mut() { frag.add_inline_context_style(enclosing_style.clone()); } } @@ -289,7 +298,7 @@ impl<'a> FlowConstructor<'a> { flow_list: &mut Vec, whitespace_stripping: WhitespaceStrippingMode, node: &ThreadSafeLayoutNode) { - let mut fragments = fragment_accumulator.finish(); + let mut fragments = fragment_accumulator.to_dlist(); if fragments.is_empty() { return }; @@ -298,14 +307,14 @@ impl<'a> FlowConstructor<'a> { NoWhitespaceStripping => {} StripWhitespaceFromStart => { flow::mut_base(flow.deref_mut()).restyle_damage.insert( - fragments.strip_ignorable_whitespace_from_start()); + strip_ignorable_whitespace_from_start(&mut fragments)); if fragments.is_empty() { return }; } StripWhitespaceFromEnd => { flow::mut_base(flow.deref_mut()).restyle_damage.insert( - fragments.strip_ignorable_whitespace_from_end()); + strip_ignorable_whitespace_from_end(&mut fragments)); if fragments.is_empty() { return }; @@ -314,7 +323,7 @@ impl<'a> FlowConstructor<'a> { // Build a list of all the inline-block fragments before fragments is moved. let mut inline_block_flows = vec!(); - for f in fragments.fragments.iter() { + for f in fragments.iter() { match f.specific { InlineBlockFragment(ref info) => inline_block_flows.push(info.flow_ref.clone()), InlineAbsoluteHypotheticalFragment(ref info) => { @@ -324,6 +333,12 @@ impl<'a> FlowConstructor<'a> { } } + // We must scan for runs before computing minimum ascent and descent because scanning + // for runs might collapse so much whitespace away that only hypothetical fragments + // remain. In that case the inline flow will compute its ascent and descent to be zero. + let fragments = TextRunScanner::new().scan_for_runs(self.layout_context.font_context(), + fragments); + let mut inline_flow_ref = FlowRef::new(box InlineFlow::from_fragments((*node).clone(), fragments)); @@ -335,10 +350,6 @@ impl<'a> FlowConstructor<'a> { { let inline_flow = inline_flow_ref.as_inline(); - // We must scan for runs before computing minimum ascent and descent because scanning - // for runs might collapse so much whitespace away that only hypothetical fragments - // remain. In that case the inline flow will compute its ascent and descent to be zero. - TextRunScanner::new().scan_for_runs(self.layout_context.font_context(), inline_flow); let (ascent, descent) = inline_flow.compute_minimum_ascent_and_descent(self.layout_context.font_context(), @@ -409,7 +420,7 @@ impl<'a> FlowConstructor<'a> { predecessors: predecessors, flow: kid_flow } = split; - inline_fragment_accumulator.fragments.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. @@ -441,7 +452,7 @@ impl<'a> FlowConstructor<'a> { } // Add the fragments to the list we're maintaining. - inline_fragment_accumulator.fragments.push_all(successor_fragments); + inline_fragment_accumulator.push_all(successor_fragments); abs_descendants.push_descendants(kid_abs_descendants); } ConstructionItemConstructionResult(WhitespaceConstructionItem(whitespace_node, @@ -451,11 +462,11 @@ impl<'a> FlowConstructor<'a> { // 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); + let fragment = Fragment::from_opaque_node_and_style(whitespace_node, + whitespace_style, + whitespace_damage, + fragment_info); + inline_fragment_accumulator.fragments.push(fragment); } ConstructionItemConstructionResult(TableColumnFragmentConstructionItem(_)) => { // TODO: Implement anonymous table objects for missing parents @@ -483,8 +494,8 @@ impl<'a> FlowConstructor<'a> { if node.get_pseudo_element_type() != Normal || node.type_id() == Some(ElementNodeTypeId(HTMLInputElementTypeId)) { let fragment_info = UnscannedTextFragment(UnscannedTextFragmentInfo::new(node)); - let mut fragment = Fragment::new_from_specific_info(node, fragment_info); - inline_fragment_accumulator.fragments.push(&mut fragment); + let fragment = Fragment::new_from_specific_info(node, fragment_info); + inline_fragment_accumulator.fragments.push(fragment); first_fragment = false; } @@ -577,7 +588,7 @@ impl<'a> FlowConstructor<'a> { predecessors: mem::replace( &mut fragment_accumulator, - InlineFragmentsAccumulator::from_inline_node(node)).finish(), + InlineFragmentsAccumulator::from_inline_node(node)).to_dlist(), flow: flow, }; opt_inline_block_splits.push(split); @@ -596,32 +607,34 @@ impl<'a> FlowConstructor<'a> { predecessors: predecessors, flow: kid_flow } = split; - fragment_accumulator.fragments.push_all(predecessors); + fragment_accumulator.push_all(predecessors); let split = InlineBlockSplit { predecessors: mem::replace(&mut fragment_accumulator, InlineFragmentsAccumulator::from_inline_node(node)) - .finish(), + .to_dlist(), flow: kid_flow, }; opt_inline_block_splits.push(split) } // Push residual fragments. - fragment_accumulator.fragments.push_all(successors); + fragment_accumulator.push_all(successors); abs_descendants.push_descendants(kid_abs_descendants); } - ConstructionItemConstructionResult(WhitespaceConstructionItem(whitespace_node, - whitespace_style, - whitespace_damage)) => { + ConstructionItemConstructionResult(WhitespaceConstructionItem( + whitespace_node, + 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, + let fragment_info = UnscannedTextFragment(UnscannedTextFragmentInfo::from_text( + " ".to_string())); + let fragment = Fragment::from_opaque_node_and_style(whitespace_node, whitespace_style, whitespace_damage, fragment_info); - fragment_accumulator.fragments.push(&mut fragment) + fragment_accumulator.fragments.push(fragment) } ConstructionItemConstructionResult(TableColumnFragmentConstructionItem(_)) => { // TODO: Implement anonymous table objects for missing parents @@ -636,7 +649,7 @@ impl<'a> FlowConstructor<'a> { let construction_item = InlineFragmentsConstructionItem( InlineFragmentsConstructionResult { splits: opt_inline_block_splits, - fragments: fragment_accumulator.finish(), + fragments: fragment_accumulator.to_dlist(), abs_descendants: abs_descendants, }); ConstructionItemConstructionResult(construction_item) @@ -668,15 +681,15 @@ impl<'a> FlowConstructor<'a> { // If this is generated content, then we need to initialize the accumulator with the // fragment corresponding to that content. Otherwise, just initialize with the ordinary // fragment that needs to be generated for this inline node. - let mut fragment = if node.get_pseudo_element_type() != Normal { + let fragment = if node.get_pseudo_element_type() != Normal { let fragment_info = UnscannedTextFragment(UnscannedTextFragmentInfo::new(node)); Fragment::new_from_specific_info(node, fragment_info) } else { Fragment::new(self, node) }; - let mut fragments = InlineFragments::new(); - fragments.push(&mut fragment); + let mut fragments = DList::new(); + fragments.push(fragment); let construction_item = InlineFragmentsConstructionItem(InlineFragmentsConstructionResult { splits: Vec::new(), @@ -695,14 +708,14 @@ impl<'a> FlowConstructor<'a> { }; let fragment_info = InlineBlockFragment(InlineBlockFragmentInfo::new(block_flow)); - let mut fragment = Fragment::new_from_specific_info(node, fragment_info); + let fragment = Fragment::new_from_specific_info(node, fragment_info); let mut fragment_accumulator = InlineFragmentsAccumulator::from_inline_node(node); - fragment_accumulator.fragments.push(&mut fragment); + fragment_accumulator.fragments.push(fragment); let construction_item = InlineFragmentsConstructionItem(InlineFragmentsConstructionResult { splits: Vec::new(), - fragments: fragment_accumulator.finish(), + fragments: fragment_accumulator.to_dlist(), abs_descendants: abs_descendants, }); ConstructionItemConstructionResult(construction_item) @@ -720,14 +733,14 @@ impl<'a> FlowConstructor<'a> { let fragment_info = InlineAbsoluteHypotheticalFragment( InlineAbsoluteHypotheticalFragmentInfo::new(block_flow)); - let mut fragment = Fragment::new_from_specific_info(node, fragment_info); + let fragment = Fragment::new_from_specific_info(node, fragment_info); let mut fragment_accumulator = InlineFragmentsAccumulator::from_inline_node(node); - fragment_accumulator.fragments.push(&mut fragment); + fragment_accumulator.fragments.push(fragment); let construction_item = InlineFragmentsConstructionItem(InlineFragmentsConstructionResult { splits: Vec::new(), - fragments: fragment_accumulator.finish(), + fragments: fragment_accumulator.to_dlist(), abs_descendants: abs_descendants, }); ConstructionItemConstructionResult(construction_item) @@ -1217,3 +1230,38 @@ impl FlowConstructionUtils for FlowRef { } } } + +/// Strips ignorable whitespace from the start of a list of fragments. +/// +/// Returns some damage that must be added to the `InlineFlow`. +pub fn strip_ignorable_whitespace_from_start(this: &mut DList) -> RestyleDamage { + if this.is_empty() { + return RestyleDamage::empty() // Fast path. + } + + let mut damage = RestyleDamage::empty(); + while !this.is_empty() && this.front().as_ref().unwrap().is_ignorable_whitespace() { + debug!("stripping ignorable whitespace from start"); + damage = RestyleDamage::all(); + drop(this.pop_front()); + } + damage +} + +/// Strips ignorable whitespace from the end of a list of fragments. +/// +/// Returns some damage that must be added to the `InlineFlow`. +pub fn strip_ignorable_whitespace_from_end(this: &mut DList) -> RestyleDamage { + if this.is_empty() { + return RestyleDamage::empty(); + } + + let mut damage = RestyleDamage::empty(); + while !this.is_empty() && this.back().as_ref().unwrap().is_ignorable_whitespace() { + debug!("stripping ignorable whitespace from end"); + damage = RestyleDamage::all(); + drop(this.pop()); + } + damage +} + diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 56b14754ca6..1546605fdf0 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -386,7 +386,8 @@ pub struct ScannedTextFragmentInfo { impl ScannedTextFragmentInfo { /// Creates the information specific to a scanned text fragment from a range and a text run. - pub fn new(run: Arc>, range: Range, content_inline_size: Au) -> ScannedTextFragmentInfo { + pub fn new(run: Arc>, range: Range, content_inline_size: Au) + -> ScannedTextFragmentInfo { ScannedTextFragmentInfo { run: run, range: range, @@ -509,7 +510,9 @@ impl Fragment { } /// Constructs a new `Fragment` instance for an anonymous table object. - pub fn new_anonymous_table_fragment(node: &ThreadSafeLayoutNode, specific: SpecificFragmentInfo) -> Fragment { + pub fn new_anonymous_table_fragment(node: &ThreadSafeLayoutNode, + specific: SpecificFragmentInfo) + -> Fragment { // CSS 2.1 § 17.2.1 This is for non-inherited properties on anonymous table fragments // example: // @@ -517,7 +520,8 @@ impl Fragment { // Foo // // - // Anonymous table fragments, TableRowFragment and TableCellFragment, are generated around `Foo`, but it shouldn't inherit the border. + // Anonymous table fragments, TableRowFragment and TableCellFragment, are generated around + // `Foo`, but they shouldn't inherit the border. let node_style = cascade_anonymous(&**node.style()); let writing_mode = node_style.writing_mode; @@ -587,14 +591,14 @@ impl Fragment { } } - /// Returns a debug ID of this fragment. This ID should not be considered stable across multiple - /// layouts or fragment manipulations. + /// 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 { self.debug_id } - /// Transforms this fragment into another fragment of the given type, with the given size, preserving all - /// the other data. + /// Transforms this fragment into another fragment of the given type, with the given size, + /// preserving all the other data. pub fn transform(&self, size: LogicalSize, mut info: ScannedTextFragmentInfo) -> Fragment { let new_border_box = LogicalRect::from_point_size(self.style.writing_mode, self.border_box.start, size); diff --git a/components/layout/inline.rs b/components/layout/inline.rs index 0babaa46234..bdfc9474fe7 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -11,7 +11,6 @@ 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; @@ -620,57 +619,6 @@ impl InlineFragments { self.fragments.get_mut(index) } - /// Strips ignorable whitespace from the start of a list of fragments. - /// - /// 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 - } - - skipping = false; - new_fragments.push(fragment); - } - - self.fragments = new_fragments; - damage - } - - /// Strips ignorable whitespace from the end of a list of fragments. - /// - /// 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 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) { diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs index fbd840fdd9c..114f9b85e84 100644 --- a/components/layout/layout_task.rs +++ b/components/layout/layout_task.rs @@ -580,7 +580,7 @@ impl LayoutTask { let current_screen_size = Size2D(Au::from_frac32_px(viewport_size.width.get()), Au::from_frac32_px(viewport_size.height.get())); - let old_screen_size = mem::replace(&mut rw_data.screen_size, current_screen_size); + rw_data.screen_size = current_screen_size; // Create a layout context for use throughout the following passes. let mut shared_layout_ctx = @@ -591,8 +591,6 @@ impl LayoutTask { // Handle conditions where the entire flow tree is invalid. let mut needs_dirtying = false; - - needs_dirtying |= current_screen_size != old_screen_size; needs_dirtying |= rw_data.stylesheet_dirty; unsafe { diff --git a/components/layout/text.rs b/components/layout/text.rs index 19338d4c021..da5fd549697 100644 --- a/components/layout/text.rs +++ b/components/layout/text.rs @@ -6,78 +6,67 @@ #![deny(unsafe_block)] -use flow::Flow; use fragment::{Fragment, ScannedTextFragmentInfo, UnscannedTextFragment}; +use inline::InlineFragments; use gfx::font::{FontMetrics,RunMetrics}; use gfx::font_context::FontContext; use gfx::text::glyph::CharIndex; use gfx::text::text_run::TextRun; -use gfx::text::util::{CompressWhitespaceNewline, transform_text, CompressNone}; +use gfx::text::util::{mod, CompressWhitespaceNewline, CompressNone}; +use servo_util::dlist; use servo_util::geometry::Au; use servo_util::logical_geometry::{LogicalSize, WritingMode}; use servo_util::range::Range; -use servo_util::smallvec::SmallVec; +use servo_util::smallvec::{SmallVec, SmallVec1}; +use std::collections::{DList, Deque}; +use std::mem; use style::ComputedValues; use style::computed_values::{line_height, text_orientation, white_space}; use style::style_structs::Font as FontStyle; use sync::Arc; -struct NewLinePositions { - new_line_pos: Vec, -} - -// A helper function. -fn can_coalesce_text_nodes(fragments: &[Fragment], left_i: uint, right_i: uint) -> bool { - assert!(left_i != right_i); - fragments[left_i].can_merge_with_fragment(&fragments[right_i]) -} - /// A stack-allocated object for scanning an inline flow into `TextRun`-containing `TextFragment`s. pub struct TextRunScanner { - pub clump: Range, + pub clump: DList, } impl TextRunScanner { pub fn new() -> TextRunScanner { TextRunScanner { - clump: Range::empty(), + clump: DList::new(), } } - pub fn scan_for_runs(&mut self, font_context: &mut FontContext, flow: &mut Flow) { - { - let inline = flow.as_immutable_inline(); - debug!("TextRunScanner: scanning {:u} fragments for text runs...", inline.fragments.len()); - } - - let fragments = &mut flow.as_inline().fragments; + pub fn scan_for_runs(&mut self, font_context: &mut FontContext, mut fragments: DList) + -> InlineFragments { + debug!("TextRunScanner: scanning {:u} fragments for text runs...", fragments.len()); + // 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. + let mut new_fragments = Vec::with_capacity(fragments.len()); let mut last_whitespace = true; - let mut new_fragments = Vec::new(); - for fragment_i in range(0, fragments.fragments.len()) { - debug!("TextRunScanner: considering fragment: {:u}", fragment_i); - if fragment_i > 0 && !can_coalesce_text_nodes(fragments.fragments.as_slice(), fragment_i - 1, fragment_i) { - last_whitespace = self.flush_clump_to_list(font_context, - fragments.fragments.as_slice(), - &mut new_fragments, - last_whitespace); + while !fragments.is_empty() { + // Create a clump. + self.clump.append(dlist::split(&mut fragments)); + while !fragments.is_empty() && self.clump + .back() + .unwrap() + .can_merge_with_fragment(fragments.front() + .unwrap()) { + self.clump.append(dlist::split(&mut fragments)); } - self.clump.extend_by(CharIndex(1)); + // Flush that clump to the list of fragments we're building up. + last_whitespace = self.flush_clump_to_list(font_context, + &mut new_fragments, + last_whitespace); } - // Handle remaining clumps. - if self.clump.length() > CharIndex(0) { - drop(self.flush_clump_to_list(font_context, - fragments.fragments.as_slice(), - &mut new_fragments, - last_whitespace)) + debug!("TextRunScanner: complete."); + InlineFragments { + fragments: new_fragments, } - - debug!("TextRunScanner: swapping out fragments."); - - fragments.fragments = new_fragments; } /// A "clump" is a range of inline flow leaves that can be merged together into a single @@ -86,178 +75,109 @@ impl TextRunScanner { /// The flow keeps track of the fragments contained by all non-leaf DOM nodes. This is necessary /// for correct painting order. Since we compress several leaf fragments here, the mapping must /// be adjusted. - /// - /// FIXME(#2267, pcwalton): Stop cloning fragments. Instead we will need to replace each - /// `in_fragment` with some smaller stub. fn flush_clump_to_list(&mut self, font_context: &mut FontContext, - in_fragments: &[Fragment], out_fragments: &mut Vec, - last_whitespace: bool) + mut last_whitespace: bool) -> bool { - assert!(self.clump.length() > CharIndex(0)); + debug!("TextRunScanner: flushing {} fragments in range", self.clump.len()); - debug!("TextRunScanner: flushing fragments in range={}", self.clump); - let is_singleton = self.clump.length() == CharIndex(1); - - let is_text_clump = match in_fragments[self.clump.begin().to_uint()].specific { - UnscannedTextFragment(_) => true, - _ => false, - }; - - let mut new_whitespace = last_whitespace; - match (is_singleton, is_text_clump) { - (false, false) => { - fail!("WAT: can't coalesce non-text nodes in flush_clump_to_list()!") + debug_assert!(!self.clump.is_empty()); + match self.clump.front().unwrap().specific { + UnscannedTextFragment(_) => {} + _ => { + debug_assert!(self.clump.len() == 1, + "WAT: can't coalesce non-text nodes in flush_clump_to_list()!"); + out_fragments.push(self.clump.pop_front().unwrap()); + return last_whitespace } - (true, false) => { - // FIXME(pcwalton): Stop cloning fragments, as above. - debug!("TextRunScanner: pushing single non-text fragment in range: {}", self.clump); - let new_fragment = in_fragments[self.clump.begin().to_uint()].clone(); - out_fragments.push(new_fragment) - }, - (true, true) => { - let old_fragment = &in_fragments[self.clump.begin().to_uint()]; - let text = match old_fragment.specific { + } + + // TODO(#177): Text run creation must account for the renderability of text by font group + // fonts. This is probably achieved by creating the font group above and then letting + // `FontGroup` decide which `Font` to stick into the text run. + // + // Concatenate all of the transformed strings together, saving the new character indices. + let mut new_ranges: SmallVec1> = SmallVec1::new(); + let mut new_line_positions: SmallVec1 = SmallVec1::new(); + let mut char_total = CharIndex(0); + let run = { + let fontgroup; + let compression; + { + let in_fragment = self.clump.front().unwrap(); + let font_style = in_fragment.style().get_font(); + fontgroup = font_context.get_layout_font_group_for_style(font_style); + compression = match in_fragment.white_space() { + white_space::normal | white_space::nowrap => CompressWhitespaceNewline, + white_space::pre => CompressNone, + } + } + + // First, transform/compress text of all the nodes. + let mut run_text = String::new(); + for in_fragment in self.clump.iter() { + let in_fragment = match in_fragment.specific { UnscannedTextFragment(ref text_fragment_info) => &text_fragment_info.text, _ => fail!("Expected an unscanned text fragment!"), }; - let font_style = old_fragment.style().get_font(); + let mut new_line_pos = Vec::new(); + let old_length = CharIndex(run_text.as_slice().char_len() as int); + last_whitespace = util::transform_text(in_fragment.as_slice(), + compression, + last_whitespace, + &mut run_text, + &mut new_line_pos); + new_line_positions.push(NewLinePositions(new_line_pos)); - let compression = match old_fragment.white_space() { - white_space::normal | white_space::nowrap => CompressWhitespaceNewline, - white_space::pre => CompressNone, - }; - - let mut new_line_pos = vec![]; - - let (transformed_text, whitespace) = transform_text(text.as_slice(), - compression, - last_whitespace, - &mut new_line_pos); - - new_whitespace = whitespace; - - if transformed_text.len() > 0 { - // TODO(#177): Text run creation must account for the renderability of text by - // font group fonts. This is probably achieved by creating the font group above - // and then letting `FontGroup` decide which `Font` to stick into the text run. - let fontgroup = font_context.get_layout_font_group_for_style(font_style); - let run = box fontgroup.create_textrun( - transformed_text.clone()); - - debug!("TextRunScanner: pushing single text fragment in range: {} ({})", - self.clump, - *text); - let range = Range::new(CharIndex(0), run.char_len()); - let new_metrics = run.metrics_for_range(&range); - let bounding_box_size = bounding_box_for_run_metrics( - &new_metrics, old_fragment.style.writing_mode); - let new_text_fragment_info = - ScannedTextFragmentInfo::new(Arc::new(run), range, old_fragment.border_box.size.inline); - let mut new_fragment = old_fragment.transform(bounding_box_size, new_text_fragment_info); - new_fragment.new_line_pos = new_line_pos; - out_fragments.push(new_fragment) - } - }, - (false, true) => { - // TODO(#177): Text run creation must account for the renderability of text by - // font group fonts. This is probably achieved by creating the font group above - // and then letting `FontGroup` decide which `Font` to stick into the text run. - let in_fragment = &in_fragments[self.clump.begin().to_uint()]; - let font_style = in_fragment.style().get_font(); - let fontgroup = font_context.get_layout_font_group_for_style(font_style); - - let compression = match in_fragment.white_space() { - white_space::normal | white_space::nowrap => CompressWhitespaceNewline, - white_space::pre => CompressNone, - }; - - let mut new_line_positions: Vec = vec![]; - - // First, transform/compress text of all the nodes. - let mut last_whitespace_in_clump = new_whitespace; - let transformed_strs: Vec = Vec::from_fn(self.clump.length().to_uint(), |i| { - let idx = CharIndex(i as int) + self.clump.begin(); - let in_fragment = match in_fragments[idx.to_uint()].specific { - UnscannedTextFragment(ref text_fragment_info) => &text_fragment_info.text, - _ => fail!("Expected an unscanned text fragment!"), - }; - - let mut new_line_pos = vec![]; - - let (new_str, new_whitespace) = transform_text(in_fragment.as_slice(), - compression, - last_whitespace_in_clump, - &mut new_line_pos); - new_line_positions.push(NewLinePositions { new_line_pos: new_line_pos }); - - last_whitespace_in_clump = new_whitespace; - new_str - }); - new_whitespace = last_whitespace_in_clump; - - // Next, concatenate all of the transformed strings together, saving the new - // character indices. - let mut run_str = String::new(); - - let mut new_ranges: Vec> = - 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); - new_ranges.push(Range::new(char_total, added_chars)); - run_str.push_str(transformed_strs[i as uint].as_slice()); - char_total = char_total + added_chars; - } - - // Now create the run. - // TextRuns contain a cycle which is usually resolved by the teardown - // sequence. If no clump takes ownership, however, it will leak. - let clump = self.clump; - let run = if clump.length() != CharIndex(0) && run_str.len() > 0 { - Some(Arc::new(box TextRun::new(&mut *fontgroup.fonts.get(0).borrow_mut(), - run_str.to_string()))) - } else { - None - }; - - // Make new fragments with the run and adjusted text indices. - debug!("TextRunScanner: pushing fragment(s) in range: {}", self.clump); - for i in clump.each_index() { - let logical_offset = i - self.clump.begin(); - let range = new_ranges[logical_offset.to_uint()]; - if range.length() == CharIndex(0) { - debug!("Elided an `UnscannedTextFragment` because it was zero-length after \ - compression; {}", in_fragments[i.to_uint()]); - continue - } - - let old_fragment = &in_fragments[i.to_uint()]; - let new_text_fragment_info = - ScannedTextFragmentInfo::new( - run.as_ref().unwrap().clone(), - range, - old_fragment.border_box.size.inline); - let new_metrics = new_text_fragment_info.run.metrics_for_range(&range); - let bounding_box_size = bounding_box_for_run_metrics( - &new_metrics, old_fragment.style.writing_mode); - let mut new_fragment = old_fragment.transform(bounding_box_size, new_text_fragment_info); - new_fragment.new_line_pos = new_line_positions[logical_offset.to_uint()].new_line_pos.clone(); - out_fragments.push(new_fragment) - } + let added_chars = CharIndex(run_text.as_slice().char_len() as int) - old_length; + new_ranges.push(Range::new(char_total, added_chars)); + char_total = char_total + added_chars; } - } // End of match. - let end = self.clump.end(); // FIXME: borrow checker workaround - self.clump.reset(end, CharIndex(0)); + // Now create the run. + // + // TextRuns contain a cycle which is usually resolved by the teardown sequence. + // If no clump takes ownership, however, it will leak. + if run_text.len() == 0 { + self.clump = DList::new(); + return last_whitespace + } + Arc::new(box TextRun::new(&mut *fontgroup.fonts.get(0).borrow_mut(), run_text)) + }; - new_whitespace - } // End of `flush_clump_to_list`. + // Make new fragments with the run and adjusted text indices. + debug!("TextRunScanner: pushing {} fragment(s)", self.clump.len()); + for (logical_offset, old_fragment) in + mem::replace(&mut self.clump, DList::new()).into_iter().enumerate() { + let range = *new_ranges.get(logical_offset); + if range.is_empty() { + debug!("Elided an `UnscannedTextFragment` because it was zero-length after \ + compression; {}", + old_fragment); + continue + } + + let text_inline_size = old_fragment.border_box.size.inline; + let new_text_fragment_info = + ScannedTextFragmentInfo::new(run.clone(), range, text_inline_size); + let new_metrics = new_text_fragment_info.run.metrics_for_range(&range); + let bounding_box_size = bounding_box_for_run_metrics(&new_metrics, + old_fragment.style.writing_mode); + let mut new_fragment = old_fragment.transform(bounding_box_size, + new_text_fragment_info); + let &NewLinePositions(ref mut new_line_positions) = + new_line_positions.get_mut(logical_offset); + new_fragment.new_line_pos = mem::replace(new_line_positions, Vec::new()); + out_fragments.push(new_fragment) + } + + last_whitespace + } } +struct NewLinePositions(Vec); #[inline] fn bounding_box_for_run_metrics(metrics: &RunMetrics, writing_mode: WritingMode) @@ -303,3 +223,5 @@ pub fn line_height_from_style(style: &ComputedValues, metrics: &FontMetrics) -> line_height::Length(l) => l } } + + diff --git a/components/layout/wrapper.rs b/components/layout/wrapper.rs index c4085522692..f41c3e9fdef 100644 --- a/components/layout/wrapper.rs +++ b/components/layout/wrapper.rs @@ -36,8 +36,9 @@ use util::{LayoutDataAccess, LayoutDataWrapper, PrivateLayoutData, OpaqueNodeMet use gfx::display_list::OpaqueNode; use script::dom::bindings::cell::{Ref, RefMut}; -use script::dom::bindings::codegen::InheritTypes::{ElementCast, HTMLIFrameElementCast, HTMLImageElementCast}; -use script::dom::bindings::codegen::InheritTypes::{HTMLInputElementCast, TextCast}; +use script::dom::bindings::codegen::InheritTypes::{ElementCast, HTMLIFrameElementCast}; +use script::dom::bindings::codegen::InheritTypes::{HTMLImageElementCast, HTMLInputElementCast}; +use script::dom::bindings::codegen::InheritTypes::{TextCast}; use script::dom::bindings::js::JS; use script::dom::element::{Element, HTMLAreaElementTypeId, HTMLAnchorElementTypeId}; use script::dom::element::{HTMLLinkElementTypeId, LayoutElementHelpers, RawLayoutElementHelpers}; @@ -498,6 +499,7 @@ impl<'le> TElement<'le> for LayoutElement<'le> { } } + #[inline] fn get_hover_state(self) -> bool { unsafe { self.element.node().get_hover_state_for_layout() @@ -511,18 +513,21 @@ impl<'le> TElement<'le> for LayoutElement<'le> { } } + #[inline] fn get_disabled_state(self) -> bool { unsafe { self.element.node().get_disabled_state_for_layout() } } + #[inline] fn get_enabled_state(self) -> bool { unsafe { self.element.node().get_enabled_state_for_layout() } } + #[inline] fn has_class(self, name: &Atom) -> bool { unsafe { self.element.has_class_for_layout(name) @@ -735,10 +740,12 @@ impl<'ln> ThreadSafeLayoutNode<'ln> { } } + #[inline] pub fn get_pseudo_element_type(&self) -> PseudoElementType { self.pseudo } + #[inline] pub fn get_normal_display(&self) -> display::T { let mut layout_data_ref = self.mutate_layout_data(); let node_layout_data_wrapper = layout_data_ref.as_mut().unwrap(); @@ -746,6 +753,7 @@ impl<'ln> ThreadSafeLayoutNode<'ln> { style.get_box().display } + #[inline] pub fn get_before_display(&self) -> display::T { let mut layout_data_ref = self.mutate_layout_data(); let node_layout_data_wrapper = layout_data_ref.as_mut().unwrap(); @@ -753,6 +761,7 @@ impl<'ln> ThreadSafeLayoutNode<'ln> { style.get_box().display } + #[inline] pub fn get_after_display(&self) -> display::T { let mut layout_data_ref = self.mutate_layout_data(); let node_layout_data_wrapper = layout_data_ref.as_mut().unwrap(); @@ -760,12 +769,14 @@ impl<'ln> ThreadSafeLayoutNode<'ln> { style.get_box().display } + #[inline] pub fn has_before_pseudo(&self) -> bool { let layout_data_wrapper = self.borrow_layout_data(); let layout_data_wrapper_ref = layout_data_wrapper.as_ref().unwrap(); layout_data_wrapper_ref.data.before_style.is_some() } + #[inline] pub fn has_after_pseudo(&self) -> bool { let layout_data_wrapper = self.borrow_layout_data(); let layout_data_wrapper_ref = layout_data_wrapper.as_ref().unwrap(); diff --git a/components/util/cache.rs b/components/util/cache.rs index dd45ee98cc0..19f861a1d3c 100644 --- a/components/util/cache.rs +++ b/components/util/cache.rs @@ -49,6 +49,13 @@ impl Cache for HashCache { fn evict_all(&mut self) { self.entries.clear(); } + +} + +impl HashCache where K: Clone + PartialEq + Eq + Hash, V: Clone { + pub fn find_equiv<'a,Q>(&'a self, key: &Q) -> Option<&'a V> where Q: Hash + Equiv { + self.entries.find_equiv(key) + } } #[test] diff --git a/components/util/dlist.rs b/components/util/dlist.rs new file mode 100644 index 00000000000..14a1b5d4cdf --- /dev/null +++ b/components/util/dlist.rs @@ -0,0 +1,68 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ + +//! Utility functions for doubly-linked lists. + +use std::collections::DList; +use std::mem; +use std::ptr; + +struct RawDList { + length: uint, + head: Option>>, + tail: *mut RawNode, +} + +#[allow(dead_code)] +struct RawNode { + next: Option>>, + prev: *mut RawNode, + value: T, +} + +#[unsafe_destructor] +impl Drop for RawDList { + fn drop(&mut self) { + fail!("shouldn't happen") + } +} + +/// Workaround for a missing method on Rust's `DList` type. Splits the head off a list in O(1) +/// time. +pub fn split(list: &mut DList) -> DList { + let list = unsafe { + mem::transmute::<&mut DList,&mut RawDList>(list) + }; + + if list.length == 0 { + fail!("split_dlist(): empty list") + } + let mut head_node = mem::replace(&mut list.head, None); + let head_node_ptr: *mut RawNode = &mut **head_node.as_mut().unwrap(); + let mut head_list = RawDList { + length: 1, + head: head_node, + tail: head_node_ptr, + }; + debug_assert!(list.head.is_none()); + mem::swap(&mut head_list.head.as_mut().unwrap().next, &mut list.head); + debug_assert!(head_list.head.as_mut().unwrap().next.is_none()); + debug_assert!(head_list.head.as_mut().unwrap().prev.is_null()); + head_list.head.as_mut().unwrap().prev = ptr::null_mut(); + + list.length -= 1; + if list.length == 0 { + list.tail = ptr::null_mut() + } else { + if list.length == 1 { + list.tail = &mut **list.head.as_mut().unwrap() as *mut RawNode + } + list.head.as_mut().unwrap().prev = ptr::null_mut() + } + + unsafe { + mem::transmute::,DList>(head_list) + } +} + diff --git a/components/util/lib.rs b/components/util/lib.rs index f5fd08dc24e..fa42518a95d 100644 --- a/components/util/lib.rs +++ b/components/util/lib.rs @@ -36,6 +36,7 @@ use std::sync::Arc; pub mod bloom; pub mod cache; pub mod debug_utils; +pub mod dlist; pub mod fnv; pub mod geometry; pub mod logical_geometry;