diff options
-rw-r--r-- | components/layout/animation.rs | 6 | ||||
-rw-r--r-- | components/layout/block.rs | 21 | ||||
-rw-r--r-- | components/layout/context.rs | 78 | ||||
-rw-r--r-- | components/layout/css/matching.rs | 351 | ||||
-rw-r--r-- | components/layout/layout_task.rs | 23 | ||||
-rw-r--r-- | components/layout/traversal.rs | 16 | ||||
-rw-r--r-- | components/script/layout_interface.rs | 37 | ||||
-rw-r--r-- | components/style/animation.rs | 23 | ||||
-rw-r--r-- | components/style/context.rs | 74 | ||||
-rw-r--r-- | components/style/lib.rs | 2 | ||||
-rw-r--r-- | components/style/matching.rs | 343 |
11 files changed, 518 insertions, 456 deletions
diff --git a/components/layout/animation.rs b/components/layout/animation.rs index 9160c317a5a..4a8932ad2e8 100644 --- a/components/layout/animation.rs +++ b/components/layout/animation.rs @@ -40,7 +40,7 @@ pub fn start_transitions_if_applicable(new_animations_sender: &Mutex<Sender<Anim let start_time = now + (animation_style.transition_delay.0.get_mod(i).seconds() as f64); new_animations_sender.lock().unwrap().send(Animation { - node: node.id(), + node: node, property_animation: property_animation, start_time: start_time, end_time: start_time + @@ -101,7 +101,7 @@ pub fn update_animation_state(constellation_chan: &ConstellationChan<Constellati // Add new running animations. for new_running_animation in new_running_animations { - match running_animations.entry(OpaqueNode(new_running_animation.node)) { + match running_animations.entry(new_running_animation.node) { Entry::Vacant(entry) => { entry.insert(vec![new_running_animation]); } @@ -126,7 +126,7 @@ pub fn recalc_style_for_animations(flow: &mut Flow, animations: &HashMap<OpaqueNode, Vec<Animation>>) { let mut damage = RestyleDamage::empty(); flow.mutate_fragments(&mut |fragment| { - if let Some(ref animations) = animations.get(&OpaqueNode(fragment.node.id())) { + if let Some(ref animations) = animations.get(&fragment.node) { for animation in *animations { update_style_for_animation(animation, &mut fragment.style, Some(&mut damage)); } diff --git a/components/layout/block.rs b/components/layout/block.rs index ebda80f4035..5b34977b484 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -56,6 +56,7 @@ use std::fmt; use std::sync::Arc; use style::computed_values::{border_collapse, box_sizing, display, float, overflow_x, overflow_y}; use style::computed_values::{position, text_align, transform_style}; +use style::context::StyleContext; use style::properties::ComputedValues; use style::values::computed::{LengthOrNone, LengthOrPercentageOrNone}; use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto}; @@ -890,7 +891,7 @@ impl BlockFlow { if is_root { let viewport_size = LogicalSize::from_physical(self.fragment.style.writing_mode, - layout_context.shared.viewport_size); + layout_context.shared_context().viewport_size); block_size = max(viewport_size.block, block_size) } @@ -1051,7 +1052,7 @@ impl BlockFlow { pub fn explicit_block_containing_size(&self, layout_context: &LayoutContext) -> Option<Au> { if self.is_root() || self.is_fixed() { let viewport_size = LogicalSize::from_physical(self.fragment.style.writing_mode, - layout_context.shared.viewport_size); + layout_context.shared_context().viewport_size); Some(viewport_size.block) } else if self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) && self.base.block_container_explicit_block_size.is_none() { @@ -1117,7 +1118,7 @@ impl BlockFlow { fn calculate_absolute_block_size_and_margins(&mut self, layout_context: &LayoutContext) { let opaque_self = OpaqueFlow::from_flow(self); let containing_block_block_size = - self.containing_block_size(&layout_context.shared.viewport_size, opaque_self).block; + self.containing_block_size(&layout_context.shared_context().viewport_size, opaque_self).block; // This is the stored content block-size value from assign-block-size let content_block_size = self.fragment.border_box.size.block; @@ -1270,7 +1271,7 @@ impl BlockFlow { // Calculate containing block inline size. let containing_block_size = if flags.contains(IS_ABSOLUTELY_POSITIONED) { - self.containing_block_size(&layout_context.shared.viewport_size, opaque_self).inline + self.containing_block_size(&layout_context.shared_context().viewport_size, opaque_self).inline } else { content_inline_size }; @@ -1603,7 +1604,7 @@ impl Flow for BlockFlow { debug!("Setting root position"); self.base.position.start = LogicalPoint::zero(self.base.writing_mode); self.base.block_container_inline_size = LogicalSize::from_physical( - self.base.writing_mode, layout_context.shared.viewport_size).inline; + self.base.writing_mode, layout_context.shared_context().viewport_size).inline; self.base.block_container_writing_mode = self.base.writing_mode; // The root element is never impacted by floats. @@ -1862,10 +1863,10 @@ impl Flow for BlockFlow { let visible_rect = match layout_context.shared.visible_rects.get(&self.layer_id()) { Some(visible_rect) => *visible_rect, - None => Rect::new(Point2D::zero(), layout_context.shared.viewport_size), + None => Rect::new(Point2D::zero(), layout_context.shared_context().viewport_size), }; - let viewport_size = layout_context.shared.viewport_size; + let viewport_size = layout_context.shared_context().viewport_size; visible_rect.inflate(viewport_size.width * DISPLAY_PORT_SIZE_FACTOR, viewport_size.height * DISPLAY_PORT_SIZE_FACTOR) } else if is_stacking_context { @@ -2591,7 +2592,7 @@ impl ISizeAndMarginsComputer for AbsoluteNonReplaced { layout_context: &LayoutContext) -> Au { let opaque_block = OpaqueFlow::from_flow(block); - block.containing_block_size(&layout_context.shared.viewport_size, opaque_block).inline + block.containing_block_size(&layout_context.shared_context().viewport_size, opaque_block).inline } fn set_inline_position_of_flow_if_necessary(&self, @@ -2703,7 +2704,7 @@ impl ISizeAndMarginsComputer for AbsoluteReplaced { -> MaybeAuto { let opaque_block = OpaqueFlow::from_flow(block); let containing_block_inline_size = - block.containing_block_size(&layout_context.shared.viewport_size, opaque_block).inline; + block.containing_block_size(&layout_context.shared_context().viewport_size, opaque_block).inline; let fragment = block.fragment(); fragment.assign_replaced_inline_size_if_necessary(containing_block_inline_size); // For replaced absolute flow, the rest of the constraint solving will @@ -2717,7 +2718,7 @@ impl ISizeAndMarginsComputer for AbsoluteReplaced { layout_context: &LayoutContext) -> Au { let opaque_block = OpaqueFlow::from_flow(block); - block.containing_block_size(&layout_context.shared.viewport_size, opaque_block).inline + block.containing_block_size(&layout_context.shared_context().viewport_size, opaque_block).inline } fn set_inline_position_of_flow_if_necessary(&self, diff --git a/components/layout/context.rs b/components/layout/context.rs index 16ca36043ad..a10fe334730 100644 --- a/components/layout/context.rs +++ b/components/layout/context.rs @@ -8,34 +8,30 @@ use app_units::Au; use canvas_traits::CanvasMsg; -use css::matching::{ApplicableDeclarationsCache, StyleSharingCandidateCache}; -use euclid::{Rect, Size2D}; +use euclid::Rect; use fnv::FnvHasher; -use gfx::display_list::OpaqueNode; use gfx::font_cache_task::FontCacheTask; use gfx::font_context::FontContext; use gfx_traits::LayerId; use ipc_channel::ipc::{self, IpcSender}; -use msg::ParseErrorReporter; use net_traits::image::base::Image; use net_traits::image_cache_task::{ImageCacheChan, ImageCacheTask, ImageResponse, ImageState}; use net_traits::image_cache_task::{UsePlaceholder}; -use script::layout_interface::{Animation, ReflowGoal}; use std::cell::{RefCell, RefMut}; use std::collections::HashMap; use std::collections::hash_state::DefaultState; use std::rc::Rc; use std::sync::mpsc::{Sender, channel}; -use std::sync::{Arc, Mutex, RwLock}; -use style::selector_matching::Stylist; +use std::sync::{Arc, Mutex}; +use style::context::{LocalStyleContext, SharedStyleContext, StyleContext}; +use style::matching::{ApplicableDeclarationsCache, StyleSharingCandidateCache}; use url::Url; use util::mem::HeapSizeOf; use util::opts; struct LocalLayoutContext { + style_context: LocalStyleContext, font_context: RefCell<FontContext>, - applicable_declarations_cache: RefCell<ApplicableDeclarationsCache>, - style_sharing_candidate_cache: RefCell<StyleSharingCandidateCache>, } impl HeapSizeOf for LocalLayoutContext { @@ -58,16 +54,18 @@ fn create_or_get_local_context(shared_layout_context: &SharedLayoutContext) LOCAL_CONTEXT_KEY.with(|r| { let mut r = r.borrow_mut(); if let Some(context) = r.clone() { - if shared_layout_context.screen_size_changed { - context.applicable_declarations_cache.borrow_mut().evict_all(); + if shared_layout_context.style_context.screen_size_changed { + context.style_context.applicable_declarations_cache.borrow_mut().evict_all(); } context } else { let font_cache_task = shared_layout_context.font_cache_task.lock().unwrap().clone(); let context = Rc::new(LocalLayoutContext { + style_context: LocalStyleContext { + applicable_declarations_cache: RefCell::new(ApplicableDeclarationsCache::new()), + style_sharing_candidate_cache: RefCell::new(StyleSharingCandidateCache::new()), + }, font_context: RefCell::new(FontContext::new(font_cache_task)), - applicable_declarations_cache: RefCell::new(ApplicableDeclarationsCache::new()), - style_sharing_candidate_cache: RefCell::new(StyleSharingCandidateCache::new()), }); *r = Some(context.clone()); context @@ -75,62 +73,28 @@ fn create_or_get_local_context(shared_layout_context: &SharedLayoutContext) }) } -pub struct StylistWrapper(pub *const Stylist); - -// FIXME(#6569) This implementation is unsound. -#[allow(unsafe_code)] -unsafe impl Sync for StylistWrapper {} - /// Layout information shared among all workers. This must be thread-safe. pub struct SharedLayoutContext { + /// Bits shared by the layout and style system. + pub style_context: SharedStyleContext, + /// The shared image cache task. pub image_cache_task: ImageCacheTask, /// A channel for the image cache to send responses to. pub image_cache_sender: Mutex<ImageCacheChan>, - /// The current viewport size. - pub viewport_size: Size2D<Au>, - - /// Screen sized changed? - pub screen_size_changed: bool, - /// Interface to the font cache task. pub font_cache_task: Mutex<FontCacheTask>, - /// The CSS selector stylist. - /// - /// FIXME(#2604): Make this no longer an unsafe pointer once we have fast `RWArc`s. - pub stylist: StylistWrapper, - /// The URL. pub url: Url, - /// Starts at zero, and increased by one every time a layout completes. - /// This can be used to easily check for invalid stale data. - pub generation: u32, - - /// A channel on which new animations that have been triggered by style recalculation can be - /// sent. - pub new_animations_sender: Mutex<Sender<Animation>>, - /// A channel to send canvas renderers to paint task, in order to correctly paint the layers pub canvas_layers_sender: Mutex<Sender<(LayerId, IpcSender<CanvasMsg>)>>, /// The visible rects for each layer, as reported to us by the compositor. pub visible_rects: Arc<HashMap<LayerId, Rect<Au>, DefaultState<FnvHasher>>>, - - /// The animations that are currently running. - pub running_animations: Arc<RwLock<HashMap<OpaqueNode, Vec<Animation>>>>, - - /// The list of animations that have expired since the last style recalculation. - pub expired_animations: Arc<RwLock<HashMap<OpaqueNode, Vec<Animation>>>>, - - /// Why is this reflow occurring - pub goal: ReflowGoal, - - ///The CSS error reporter for all CSS loaded in this layout thread - pub error_reporter: Box<ParseErrorReporter + Sync> } pub struct LayoutContext<'a> { @@ -138,6 +102,16 @@ pub struct LayoutContext<'a> { cached_local_layout_context: Rc<LocalLayoutContext>, } +impl<'a> StyleContext<'a> for LayoutContext<'a> { + fn shared_context(&self) -> &'a SharedStyleContext { + &self.shared.style_context + } + + fn local_context(&self) -> &LocalStyleContext { + &self.cached_local_layout_context.style_context + } +} + impl<'a> LayoutContext<'a> { pub fn new(shared_layout_context: &'a SharedLayoutContext) -> LayoutContext<'a> { @@ -156,12 +130,12 @@ impl<'a> LayoutContext<'a> { #[inline(always)] pub fn applicable_declarations_cache(&self) -> RefMut<ApplicableDeclarationsCache> { - self.cached_local_layout_context.applicable_declarations_cache.borrow_mut() + self.local_context().applicable_declarations_cache.borrow_mut() } #[inline(always)] pub fn style_sharing_candidate_cache(&self) -> RefMut<StyleSharingCandidateCache> { - self.cached_local_layout_context.style_sharing_candidate_cache.borrow_mut() + self.local_context().style_sharing_candidate_cache.borrow_mut() } pub fn get_or_request_image(&self, url: Url, use_placeholder: UsePlaceholder) diff --git a/components/layout/css/matching.rs b/components/layout/css/matching.rs index a9c07848309..e83899e2893 100644 --- a/components/layout/css/matching.rs +++ b/components/layout/css/matching.rs @@ -13,347 +13,19 @@ use incremental::{self, RestyleDamage}; use msg::ParseErrorReporter; use script::layout_interface::Animation; use selectors::bloom::BloomFilter; -use selectors::matching::{CommonStyleAffectingAttributeMode, CommonStyleAffectingAttributes}; -use selectors::matching::{common_style_affecting_attributes, rare_style_affecting_attributes}; use selectors::parser::PseudoElement; use selectors::{Element}; -use smallvec::SmallVec; -use std::borrow::ToOwned; -use std::hash::{Hash, Hasher}; use std::mem::transmute; -use std::slice::Iter; use std::sync::mpsc::Sender; use std::sync::{Arc, Mutex}; -use string_cache::{Atom, Namespace}; use style::data::PrivateStyleData; use style::dom::{TElement, TNode}; -use style::properties::{ComputedValues, PropertyDeclaration, cascade}; +use style::matching::{ApplicableDeclarations, ApplicableDeclarationsCache}; +use style::matching::{StyleSharingCandidate, StyleSharingCandidateCache}; +use style::properties::{ComputedValues, cascade}; use style::selector_matching::{DeclarationBlock, Stylist}; use util::arc_ptr_eq; -use util::cache::{LRUCache, SimpleHashCache}; use util::opts; -use util::vec::ForgetfulSink; - -pub struct ApplicableDeclarations { - pub normal: SmallVec<[DeclarationBlock; 16]>, - pub before: Vec<DeclarationBlock>, - pub after: Vec<DeclarationBlock>, - - /// Whether the `normal` declarations are shareable with other nodes. - pub normal_shareable: bool, -} - -impl ApplicableDeclarations { - pub fn new() -> ApplicableDeclarations { - ApplicableDeclarations { - normal: SmallVec::new(), - before: Vec::new(), - after: Vec::new(), - normal_shareable: false, - } - } -} - -#[derive(Clone)] -pub struct ApplicableDeclarationsCacheEntry { - pub declarations: Vec<DeclarationBlock>, -} - -impl ApplicableDeclarationsCacheEntry { - fn new(declarations: Vec<DeclarationBlock>) -> ApplicableDeclarationsCacheEntry { - ApplicableDeclarationsCacheEntry { - declarations: declarations, - } - } -} - -impl PartialEq for ApplicableDeclarationsCacheEntry { - fn eq(&self, other: &ApplicableDeclarationsCacheEntry) -> bool { - let this_as_query = ApplicableDeclarationsCacheQuery::new(&*self.declarations); - this_as_query.eq(other) - } -} -impl Eq for ApplicableDeclarationsCacheEntry {} - -impl Hash for ApplicableDeclarationsCacheEntry { - fn hash<H: Hasher>(&self, state: &mut H) { - let tmp = ApplicableDeclarationsCacheQuery::new(&*self.declarations); - tmp.hash(state); - } -} - -struct ApplicableDeclarationsCacheQuery<'a> { - declarations: &'a [DeclarationBlock], -} - -impl<'a> ApplicableDeclarationsCacheQuery<'a> { - fn new(declarations: &'a [DeclarationBlock]) -> ApplicableDeclarationsCacheQuery<'a> { - ApplicableDeclarationsCacheQuery { - declarations: declarations, - } - } -} - -impl<'a> PartialEq for ApplicableDeclarationsCacheQuery<'a> { - fn eq(&self, other: &ApplicableDeclarationsCacheQuery<'a>) -> bool { - if self.declarations.len() != other.declarations.len() { - return false - } - for (this, other) in self.declarations.iter().zip(other.declarations) { - if !arc_ptr_eq(&this.declarations, &other.declarations) { - return false - } - } - true - } -} -impl<'a> Eq for ApplicableDeclarationsCacheQuery<'a> {} - -impl<'a> PartialEq<ApplicableDeclarationsCacheEntry> for ApplicableDeclarationsCacheQuery<'a> { - fn eq(&self, other: &ApplicableDeclarationsCacheEntry) -> bool { - let other_as_query = ApplicableDeclarationsCacheQuery::new(&other.declarations); - self.eq(&other_as_query) - } -} - -impl<'a> Hash for ApplicableDeclarationsCacheQuery<'a> { - fn hash<H: Hasher>(&self, state: &mut H) { - for declaration in self.declarations { - // Each declaration contians an Arc, which is a stable - // pointer; we use that for hashing and equality. - let ptr = &*declaration.declarations as *const Vec<PropertyDeclaration>; - ptr.hash(state); - } - } -} - -static APPLICABLE_DECLARATIONS_CACHE_SIZE: usize = 32; - -pub struct ApplicableDeclarationsCache { - cache: SimpleHashCache<ApplicableDeclarationsCacheEntry, Arc<ComputedValues>>, -} - -impl ApplicableDeclarationsCache { - pub fn new() -> ApplicableDeclarationsCache { - ApplicableDeclarationsCache { - cache: SimpleHashCache::new(APPLICABLE_DECLARATIONS_CACHE_SIZE), - } - } - - fn find(&self, declarations: &[DeclarationBlock]) -> Option<Arc<ComputedValues>> { - match self.cache.find(&ApplicableDeclarationsCacheQuery::new(declarations)) { - None => None, - Some(ref values) => Some((*values).clone()), - } - } - - fn insert(&mut self, declarations: Vec<DeclarationBlock>, style: Arc<ComputedValues>) { - self.cache.insert(ApplicableDeclarationsCacheEntry::new(declarations), style) - } - - pub fn evict_all(&mut self) { - self.cache.evict_all(); - } -} - -/// An LRU cache of the last few nodes seen, so that we can aggressively try to reuse their styles. -pub struct StyleSharingCandidateCache { - cache: LRUCache<StyleSharingCandidate, ()>, -} - -fn create_common_style_affecting_attributes_from_element<'le, E: TElement<'le>>(element: &E) - -> CommonStyleAffectingAttributes { - let mut flags = CommonStyleAffectingAttributes::empty(); - for attribute_info in &common_style_affecting_attributes() { - match attribute_info.mode { - CommonStyleAffectingAttributeMode::IsPresent(flag) => { - if element.get_attr(&ns!(), &attribute_info.atom).is_some() { - flags.insert(flag) - } - } - CommonStyleAffectingAttributeMode::IsEqual(target_value, flag) => { - match element.get_attr(&ns!(), &attribute_info.atom) { - Some(element_value) if element_value == target_value => { - flags.insert(flag) - } - _ => {} - } - } - } - } - flags -} - -#[derive(Clone)] -pub struct StyleSharingCandidate { - pub style: Arc<ComputedValues>, - pub parent_style: Arc<ComputedValues>, - pub local_name: Atom, - // FIXME(pcwalton): Should be a list of atoms instead. - pub class: Option<String>, - pub namespace: Namespace, - pub common_style_affecting_attributes: CommonStyleAffectingAttributes, - pub link: bool, -} - -impl PartialEq for StyleSharingCandidate { - fn eq(&self, other: &StyleSharingCandidate) -> bool { - arc_ptr_eq(&self.style, &other.style) && - arc_ptr_eq(&self.parent_style, &other.parent_style) && - self.local_name == other.local_name && - self.class == other.class && - self.link == other.link && - self.namespace == other.namespace && - self.common_style_affecting_attributes == other.common_style_affecting_attributes - } -} - -impl StyleSharingCandidate { - /// Attempts to create a style sharing candidate from this node. Returns - /// the style sharing candidate or `None` if this node is ineligible for - /// style sharing. - fn new<'le, E: TElement<'le>>(element: &E) -> Option<StyleSharingCandidate> { - let parent_element = match element.parent_element() { - None => return None, - Some(parent_element) => parent_element, - }; - - let style = unsafe { - match element.as_node().borrow_data_unchecked() { - None => return None, - Some(data_ref) => { - match (*data_ref).style { - None => return None, - Some(ref data) => (*data).clone(), - } - } - } - }; - let parent_style = unsafe { - match parent_element.as_node().borrow_data_unchecked() { - None => return None, - Some(parent_data_ref) => { - match (*parent_data_ref).style { - None => return None, - Some(ref data) => (*data).clone(), - } - } - } - }; - - if element.style_attribute().is_some() { - return None - } - - Some(StyleSharingCandidate { - style: style, - parent_style: parent_style, - local_name: element.get_local_name().clone(), - class: element.get_attr(&ns!(), &atom!("class")) - .map(|string| string.to_owned()), - link: element.is_link(), - namespace: (*element.get_namespace()).clone(), - common_style_affecting_attributes: - create_common_style_affecting_attributes_from_element::<'le, E>(&element) - }) - } - - fn can_share_style_with<'a, E: TElement<'a>>(&self, element: &E) -> bool { - if *element.get_local_name() != self.local_name { - return false - } - - // FIXME(pcwalton): Use `each_class` here instead of slow string comparison. - match (&self.class, element.get_attr(&ns!(), &atom!("class"))) { - (&None, Some(_)) | (&Some(_), None) => return false, - (&Some(ref this_class), Some(element_class)) if - element_class != &**this_class => { - return false - } - (&Some(_), Some(_)) | (&None, None) => {} - } - - if *element.get_namespace() != self.namespace { - return false - } - - let mut matching_rules = ForgetfulSink::new(); - element.synthesize_presentational_hints_for_legacy_attributes(&mut matching_rules); - if !matching_rules.is_empty() { - return false; - } - - // FIXME(pcwalton): It's probably faster to iterate over all the element's attributes and - // use the {common, rare}-style-affecting-attributes tables as lookup tables. - - for attribute_info in &common_style_affecting_attributes() { - match attribute_info.mode { - CommonStyleAffectingAttributeMode::IsPresent(flag) => { - if self.common_style_affecting_attributes.contains(flag) != - element.get_attr(&ns!(), &attribute_info.atom).is_some() { - return false - } - } - CommonStyleAffectingAttributeMode::IsEqual(target_value, flag) => { - match element.get_attr(&ns!(), &attribute_info.atom) { - Some(ref element_value) if self.common_style_affecting_attributes - .contains(flag) && - *element_value != target_value => { - return false - } - Some(_) if !self.common_style_affecting_attributes.contains(flag) => { - return false - } - None if self.common_style_affecting_attributes.contains(flag) => { - return false - } - _ => {} - } - } - } - } - - for attribute_name in &rare_style_affecting_attributes() { - if element.get_attr(&ns!(), attribute_name).is_some() { - return false - } - } - - if element.is_link() != self.link { - return false - } - - // TODO(pcwalton): We don't support visited links yet, but when we do there will need to - // be some logic here. - - true - } -} - -static STYLE_SHARING_CANDIDATE_CACHE_SIZE: usize = 40; - -impl StyleSharingCandidateCache { - pub fn new() -> StyleSharingCandidateCache { - StyleSharingCandidateCache { - cache: LRUCache::new(STYLE_SHARING_CANDIDATE_CACHE_SIZE), - } - } - - pub fn iter(&self) -> Iter<(StyleSharingCandidate, ())> { - self.cache.iter() - } - - pub fn insert_if_possible<'le, E: TElement<'le>>(&mut self, element: &E) { - match StyleSharingCandidate::new(element) { - None => {} - Some(candidate) => self.cache.insert(candidate, ()) - } - } - - pub fn touch(&mut self, index: usize) { - self.cache.touch(index) - } -} /// The results of attempting to share a style. pub enum StyleSharingResult { @@ -454,22 +126,22 @@ impl<'ln, ConcreteNode> PrivateMatchMethods for ConcreteNode None => None, Some(ref style) => Some(&**style), }; - let (the_style, is_cacheable) = cascade(layout_context.viewport_size, + let (the_style, is_cacheable) = cascade(layout_context.style_context.viewport_size, applicable_declarations, shareable, Some(&***parent_style), cached_computed_values, - layout_context.error_reporter.clone()); + layout_context.style_context.error_reporter.clone()); cacheable = cacheable && is_cacheable; this_style = the_style } None => { - let (the_style, is_cacheable) = cascade(layout_context.viewport_size, + let (the_style, is_cacheable) = cascade(layout_context.style_context.viewport_size, applicable_declarations, shareable, None, None, - layout_context.error_reporter.clone()); + layout_context.style_context.error_reporter.clone()); cacheable = cacheable && is_cacheable; this_style = the_style } @@ -516,7 +188,7 @@ impl<'ln, ConcreteNode> PrivateMatchMethods for ConcreteNode let this_opaque = self.opaque(); let had_animations_to_expire; { - let all_expired_animations = layout_context.expired_animations.read().unwrap(); + let all_expired_animations = layout_context.style_context.expired_animations.read().unwrap(); let animations_to_expire = all_expired_animations.get(&this_opaque); had_animations_to_expire = animations_to_expire.is_some(); if let Some(ref animations) = animations_to_expire { @@ -527,17 +199,18 @@ impl<'ln, ConcreteNode> PrivateMatchMethods for ConcreteNode } if had_animations_to_expire { - layout_context.expired_animations.write().unwrap().remove(&this_opaque); + layout_context.style_context.expired_animations.write().unwrap().remove(&this_opaque); } // Merge any running transitions into the current style, and cancel them. - let had_running_animations = layout_context.running_animations + let had_running_animations = layout_context.style_context + .running_animations .read() .unwrap() .get(&this_opaque) .is_some(); if had_running_animations { - let mut all_running_animations = layout_context.running_animations.write().unwrap(); + let mut all_running_animations = layout_context.style_context.running_animations.write().unwrap(); for running_animation in all_running_animations.get(&this_opaque).unwrap() { animation::update_style_for_animation(running_animation, style, None); } diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs index 64bf9f1329c..87f34ce2a8c 100644 --- a/components/layout/layout_task.rs +++ b/components/layout/layout_task.rs @@ -12,7 +12,7 @@ use app_units::Au; use azure::azure::AzColor; use canvas_traits::CanvasMsg; use construct::ConstructionResult; -use context::{SharedLayoutContext, StylistWrapper, heap_size_of_local_context}; +use context::{SharedLayoutContext, heap_size_of_local_context}; use display_list_builder::ToGfxColor; use euclid::Matrix4; use euclid::point::Point2D; @@ -64,6 +64,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::mpsc::{channel, Sender, Receiver}; use std::sync::{Arc, Mutex, MutexGuard, RwLock}; use style::computed_values::{filter, mix_blend_mode}; +use style::context::{SharedStyleContext, StylistWrapper}; use style::dom::{TDocument, TElement, TNode}; use style::media_queries::{Device, MediaType}; use style::selector_matching::{Stylist, USER_OR_USER_AGENT_STYLESHEETS}; @@ -470,21 +471,23 @@ impl LayoutTask { goal: ReflowGoal) -> SharedLayoutContext { SharedLayoutContext { + style_context: SharedStyleContext { + viewport_size: self.viewport_size.clone(), + screen_size_changed: screen_size_changed, + stylist: StylistWrapper(&*rw_data.stylist), + generation: self.generation, + goal: goal, + new_animations_sender: Mutex::new(self.new_animations_sender.clone()), + running_animations: self.running_animations.clone(), + expired_animations: self.expired_animations.clone(), + error_reporter: self.error_reporter.clone(), + }, image_cache_task: self.image_cache_task.clone(), image_cache_sender: Mutex::new(self.image_cache_sender.clone()), - viewport_size: self.viewport_size.clone(), - screen_size_changed: screen_size_changed, font_cache_task: Mutex::new(self.font_cache_task.clone()), canvas_layers_sender: Mutex::new(self.canvas_layers_sender.clone()), - stylist: StylistWrapper(&*rw_data.stylist), url: (*url).clone(), visible_rects: self.visible_rects.clone(), - generation: self.generation, - new_animations_sender: Mutex::new(self.new_animations_sender.clone()), - goal: goal, - running_animations: self.running_animations.clone(), - expired_animations: self.expired_animations.clone(), - error_reporter: self.error_reporter.clone(), } } diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs index 007b3a3626d..c5bf7d482f3 100644 --- a/components/layout/traversal.rs +++ b/components/layout/traversal.rs @@ -6,7 +6,7 @@ use construct::FlowConstructor; use context::LayoutContext; -use css::matching::{ApplicableDeclarations, ElementMatchMethods, MatchMethods, StyleSharingResult}; +use css::matching::{ElementMatchMethods, MatchMethods, StyleSharingResult}; use flow::{PostorderFlowTraversal, PreorderFlowTraversal}; use flow::{self, Flow}; use gfx::display_list::OpaqueNode; @@ -15,7 +15,9 @@ use script::layout_interface::ReflowGoal; use selectors::bloom::BloomFilter; use std::cell::RefCell; use std::mem; +use style::context::StyleContext; use style::dom::UnsafeNode; +use style::matching::ApplicableDeclarations; use util::opts; use util::tid::tid; use wrapper::{LayoutNode, ThreadSafeLayoutNode}; @@ -72,7 +74,7 @@ fn take_task_local_bloom_filter<'ln, N>(parent_node: Option<N>, // Found cached bloom filter. (Some(parent), Some((mut bloom_filter, old_node, old_generation))) => { if old_node == parent.to_unsafe() && - old_generation == layout_context.shared.generation { + old_generation == layout_context.shared_context().generation { // Hey, the cached parent is our parent! We can reuse the bloom filter. debug!("[{}] Parent matches (={}). Reusing bloom filter.", tid(), old_node.0); } else { @@ -93,7 +95,7 @@ fn put_task_local_bloom_filter(bf: Box<BloomFilter>, STYLE_BLOOM.with(move |style_bloom| { assert!(style_bloom.borrow().is_none(), "Putting into a never-taken task-local bloom filter"); - *style_bloom.borrow_mut() = Some((bf, *unsafe_node, layout_context.shared.generation)); + *style_bloom.borrow_mut() = Some((bf, *unsafe_node, layout_context.shared_context().generation)); }) } @@ -192,7 +194,7 @@ impl<'a, 'ln, ConcreteLayoutNode> PreorderDomTraversal<'ln, ConcreteLayoutNode> let shareable_element = match node.as_element() { Some(element) => { // Perform the CSS selector matching. - let stylist = unsafe { &*self.layout_context.shared.stylist.0 }; + let stylist = unsafe { &*self.layout_context.shared_context().stylist.0 }; if element.match_element(stylist, Some(&*bf), &mut applicable_declarations) { @@ -216,7 +218,7 @@ impl<'a, 'ln, ConcreteLayoutNode> PreorderDomTraversal<'ln, ConcreteLayoutNode> parent_opt, &applicable_declarations, &mut self.layout_context.applicable_declarations_cache(), - &self.layout_context.shared.new_animations_sender); + &self.layout_context.shared_context().new_animations_sender); } // Add ourselves to the LRU cache. @@ -292,7 +294,7 @@ impl<'a, 'ln, ConcreteLayoutNode> PostorderDomTraversal<'ln, ConcreteLayoutNode> }); assert_eq!(old_node, unsafe_layout_node); - assert_eq!(old_generation, self.layout_context.shared.generation); + assert_eq!(old_generation, self.layout_context.shared_context().generation); match node.layout_parent_node(self.root) { None => { @@ -401,6 +403,6 @@ impl<'a> PostorderFlowTraversal for BuildDisplayList<'a> { #[inline] fn should_process(&self, _: &mut Flow) -> bool { - self.layout_context.shared.goal == ReflowGoal::ForDisplay + self.layout_context.shared_context().goal == ReflowGoal::ForDisplay } } diff --git a/components/script/layout_interface.rs b/components/script/layout_interface.rs index 81f43953d17..5f6fd653a7e 100644 --- a/components/script/layout_interface.rs +++ b/components/script/layout_interface.rs @@ -12,7 +12,6 @@ use euclid::point::Point2D; use euclid::rect::Rect; use gfx_traits::LayerId; use ipc_channel::ipc::{IpcReceiver, IpcSender}; -use libc::uintptr_t; use msg::compositor_msg::Epoch; use msg::constellation_msg::{ConstellationChan, Failure, PipelineId}; use msg::constellation_msg::{WindowSizeData}; @@ -25,11 +24,12 @@ use std::any::Any; use std::sync::Arc; use std::sync::mpsc::{Receiver, Sender, channel}; use string_cache::Atom; -use style::animation::PropertyAnimation; use style::stylesheets::Stylesheet; use url::Url; use util::ipc::OptionalOpaqueIpcSender; +pub use style::animation::Animation; +pub use style::context::ReflowGoal; pub use dom::node::TrustedNodeAddress; /// Asynchronous messages that script can send to layout. @@ -138,15 +138,6 @@ impl OffsetParentResponse { } } -/// Why we're doing reflow. -#[derive(PartialEq, Copy, Clone, Debug)] -pub enum ReflowGoal { - /// We're reflowing in order to send a display list to the screen. - ForDisplay, - /// We're reflowing in order to satisfy a script query. No display list will be created. - ForScriptQuery, -} - /// Any query to perform with this reflow. #[derive(PartialEq)] pub enum ReflowQueryType { @@ -226,30 +217,6 @@ impl ScriptLayoutChan for OpaqueScriptLayoutChannel { } } -/// Type of an opaque node. -pub type OpaqueNode = uintptr_t; - -/// State relating to an animation. -#[derive(Clone)] -pub struct Animation { - /// An opaque reference to the DOM node participating in the animation. - pub node: OpaqueNode, - /// A description of the property animation that is occurring. - pub property_animation: PropertyAnimation, - /// The start time of the animation, as returned by `time::precise_time_s()`. - pub start_time: f64, - /// The end time of the animation, as returned by `time::precise_time_s()`. - pub end_time: f64, -} - -impl Animation { - /// Returns the duration of this animation in seconds. - #[inline] - pub fn duration(&self) -> f64 { - self.end_time - self.start_time - } -} - pub struct NewLayoutTaskInfo { pub id: PipelineId, pub url: Url, diff --git a/components/style/animation.rs b/components/style/animation.rs index 42704700c4d..374b5396bee 100644 --- a/components/style/animation.rs +++ b/components/style/animation.rs @@ -4,6 +4,7 @@ use app_units::Au; use cssparser::{Color, RGBA}; +use dom::OpaqueNode; use euclid::point::Point2D; use properties::ComputedValues; use properties::longhands::background_position::computed_value::T as BackgroundPosition; @@ -30,6 +31,28 @@ use values::CSSFloat; use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone}; use values::computed::{CalcLengthOrPercentage, Length, LengthOrPercentage, Time}; +/// State relating to an animation. +#[derive(Clone)] +pub struct Animation { + /// An opaque reference to the DOM node participating in the animation. + pub node: OpaqueNode, + /// A description of the property animation that is occurring. + pub property_animation: PropertyAnimation, + /// The start time of the animation, as returned by `time::precise_time_s()`. + pub start_time: f64, + /// The end time of the animation, as returned by `time::precise_time_s()`. + pub end_time: f64, +} + +impl Animation { + /// Returns the duration of this animation in seconds. + #[inline] + pub fn duration(&self) -> f64 { + self.end_time - self.start_time + } +} + + #[derive(Clone, Debug)] pub struct PropertyAnimation { property: AnimatedProperty, diff --git a/components/style/context.rs b/components/style/context.rs new file mode 100644 index 00000000000..772b53f1f3d --- /dev/null +++ b/components/style/context.rs @@ -0,0 +1,74 @@ +/* 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/. */ + +use animation::Animation; +use app_units::Au; +use dom::OpaqueNode; +use euclid::Size2D; +use matching::{ApplicableDeclarationsCache, StyleSharingCandidateCache}; +use msg::ParseErrorReporter; +use selector_matching::Stylist; +use std::cell::RefCell; +use std::collections::HashMap; +use std::sync::mpsc::Sender; +use std::sync::{Arc, Mutex, RwLock}; + +pub struct StylistWrapper(pub *const Stylist); + +// FIXME(#6569) This implementation is unsound. +#[allow(unsafe_code)] +unsafe impl Sync for StylistWrapper {} + +pub struct SharedStyleContext { + /// The current viewport size. + pub viewport_size: Size2D<Au>, + + /// Screen sized changed? + pub screen_size_changed: bool, + + /// The CSS selector stylist. + /// + /// FIXME(#2604): Make this no longer an unsafe pointer once we have fast `RWArc`s. + pub stylist: StylistWrapper, + + /// Starts at zero, and increased by one every time a layout completes. + /// This can be used to easily check for invalid stale data. + pub generation: u32, + + /// A channel on which new animations that have been triggered by style recalculation can be + /// sent. + pub new_animations_sender: Mutex<Sender<Animation>>, + + /// Why is this reflow occurring + pub goal: ReflowGoal, + + /// The animations that are currently running. + pub running_animations: Arc<RwLock<HashMap<OpaqueNode, Vec<Animation>>>>, + + /// The list of animations that have expired since the last style recalculation. + pub expired_animations: Arc<RwLock<HashMap<OpaqueNode, Vec<Animation>>>>, + + ///The CSS error reporter for all CSS loaded in this layout thread + pub error_reporter: Box<ParseErrorReporter + Sync>, +} + +pub struct LocalStyleContext { + pub applicable_declarations_cache: RefCell<ApplicableDeclarationsCache>, + pub style_sharing_candidate_cache: RefCell<StyleSharingCandidateCache>, +} + +pub trait StyleContext<'a> { + fn shared_context(&self) -> &'a SharedStyleContext; + fn local_context(&self) -> &LocalStyleContext; +} + +/// Why we're doing reflow. +#[derive(PartialEq, Copy, Clone, Debug)] +pub enum ReflowGoal { + /// We're reflowing in order to send a display list to the screen. + ForDisplay, + /// We're reflowing in order to satisfy a script query. No display list will be created. + ForScriptQuery, +} + diff --git a/components/style/lib.rs b/components/style/lib.rs index 81c34b4db5a..dae26c5b943 100644 --- a/components/style/lib.rs +++ b/components/style/lib.rs @@ -43,10 +43,12 @@ extern crate util; pub mod animation; pub mod attr; +pub mod context; mod custom_properties; pub mod data; pub mod dom; pub mod font_face; +pub mod matching; pub mod media_queries; pub mod parser; pub mod restyle_hints; diff --git a/components/style/matching.rs b/components/style/matching.rs new file mode 100644 index 00000000000..3102e9bd733 --- /dev/null +++ b/components/style/matching.rs @@ -0,0 +1,343 @@ +/* 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/. */ + +use dom::{TElement, TNode}; +use properties::{ComputedValues, PropertyDeclaration}; +use selector_matching::DeclarationBlock; +use selectors::matching::{CommonStyleAffectingAttributeMode, CommonStyleAffectingAttributes}; +use selectors::matching::{common_style_affecting_attributes, rare_style_affecting_attributes}; +use smallvec::SmallVec; +use std::hash::{Hash, Hasher}; +use std::slice::Iter; +use std::sync::Arc; +use string_cache::{Atom, Namespace}; +use util::arc_ptr_eq; +use util::cache::{LRUCache, SimpleHashCache}; +use util::vec::ForgetfulSink; + +/// Pieces of layout/css/matching.rs, which will eventually be merged +/// into this file. + +fn create_common_style_affecting_attributes_from_element<'le, E: TElement<'le>>(element: &E) + -> CommonStyleAffectingAttributes { + let mut flags = CommonStyleAffectingAttributes::empty(); + for attribute_info in &common_style_affecting_attributes() { + match attribute_info.mode { + CommonStyleAffectingAttributeMode::IsPresent(flag) => { + if element.get_attr(&ns!(), &attribute_info.atom).is_some() { + flags.insert(flag) + } + } + CommonStyleAffectingAttributeMode::IsEqual(target_value, flag) => { + match element.get_attr(&ns!(), &attribute_info.atom) { + Some(element_value) if element_value == target_value => { + flags.insert(flag) + } + _ => {} + } + } + } + } + flags +} + +pub struct ApplicableDeclarations { + pub normal: SmallVec<[DeclarationBlock; 16]>, + pub before: Vec<DeclarationBlock>, + pub after: Vec<DeclarationBlock>, + + /// Whether the `normal` declarations are shareable with other nodes. + pub normal_shareable: bool, +} + +impl ApplicableDeclarations { + pub fn new() -> ApplicableDeclarations { + ApplicableDeclarations { + normal: SmallVec::new(), + before: Vec::new(), + after: Vec::new(), + normal_shareable: false, + } + } +} + +#[derive(Clone)] +pub struct ApplicableDeclarationsCacheEntry { + pub declarations: Vec<DeclarationBlock>, +} + +impl ApplicableDeclarationsCacheEntry { + fn new(declarations: Vec<DeclarationBlock>) -> ApplicableDeclarationsCacheEntry { + ApplicableDeclarationsCacheEntry { + declarations: declarations, + } + } +} + +impl PartialEq for ApplicableDeclarationsCacheEntry { + fn eq(&self, other: &ApplicableDeclarationsCacheEntry) -> bool { + let this_as_query = ApplicableDeclarationsCacheQuery::new(&*self.declarations); + this_as_query.eq(other) + } +} +impl Eq for ApplicableDeclarationsCacheEntry {} + +impl Hash for ApplicableDeclarationsCacheEntry { + fn hash<H: Hasher>(&self, state: &mut H) { + let tmp = ApplicableDeclarationsCacheQuery::new(&*self.declarations); + tmp.hash(state); + } +} + +struct ApplicableDeclarationsCacheQuery<'a> { + declarations: &'a [DeclarationBlock], +} + +impl<'a> ApplicableDeclarationsCacheQuery<'a> { + fn new(declarations: &'a [DeclarationBlock]) -> ApplicableDeclarationsCacheQuery<'a> { + ApplicableDeclarationsCacheQuery { + declarations: declarations, + } + } +} + +impl<'a> PartialEq for ApplicableDeclarationsCacheQuery<'a> { + fn eq(&self, other: &ApplicableDeclarationsCacheQuery<'a>) -> bool { + if self.declarations.len() != other.declarations.len() { + return false + } + for (this, other) in self.declarations.iter().zip(other.declarations) { + if !arc_ptr_eq(&this.declarations, &other.declarations) { + return false + } + } + true + } +} +impl<'a> Eq for ApplicableDeclarationsCacheQuery<'a> {} + +impl<'a> PartialEq<ApplicableDeclarationsCacheEntry> for ApplicableDeclarationsCacheQuery<'a> { + fn eq(&self, other: &ApplicableDeclarationsCacheEntry) -> bool { + let other_as_query = ApplicableDeclarationsCacheQuery::new(&other.declarations); + self.eq(&other_as_query) + } +} + +impl<'a> Hash for ApplicableDeclarationsCacheQuery<'a> { + fn hash<H: Hasher>(&self, state: &mut H) { + for declaration in self.declarations { + // Each declaration contians an Arc, which is a stable + // pointer; we use that for hashing and equality. + let ptr = &*declaration.declarations as *const Vec<PropertyDeclaration>; + ptr.hash(state); + } + } +} + +static APPLICABLE_DECLARATIONS_CACHE_SIZE: usize = 32; + +pub struct ApplicableDeclarationsCache { + cache: SimpleHashCache<ApplicableDeclarationsCacheEntry, Arc<ComputedValues>>, +} + +impl ApplicableDeclarationsCache { + pub fn new() -> ApplicableDeclarationsCache { + ApplicableDeclarationsCache { + cache: SimpleHashCache::new(APPLICABLE_DECLARATIONS_CACHE_SIZE), + } + } + + pub fn find(&self, declarations: &[DeclarationBlock]) -> Option<Arc<ComputedValues>> { + match self.cache.find(&ApplicableDeclarationsCacheQuery::new(declarations)) { + None => None, + Some(ref values) => Some((*values).clone()), + } + } + + pub fn insert(&mut self, declarations: Vec<DeclarationBlock>, style: Arc<ComputedValues>) { + self.cache.insert(ApplicableDeclarationsCacheEntry::new(declarations), style) + } + + pub fn evict_all(&mut self) { + self.cache.evict_all(); + } +} + +/// An LRU cache of the last few nodes seen, so that we can aggressively try to reuse their styles. +pub struct StyleSharingCandidateCache { + cache: LRUCache<StyleSharingCandidate, ()>, +} + +#[derive(Clone)] +pub struct StyleSharingCandidate { + pub style: Arc<ComputedValues>, + pub parent_style: Arc<ComputedValues>, + pub local_name: Atom, + // FIXME(pcwalton): Should be a list of atoms instead. + pub class: Option<String>, + pub namespace: Namespace, + pub common_style_affecting_attributes: CommonStyleAffectingAttributes, + pub link: bool, +} + +impl PartialEq for StyleSharingCandidate { + fn eq(&self, other: &StyleSharingCandidate) -> bool { + arc_ptr_eq(&self.style, &other.style) && + arc_ptr_eq(&self.parent_style, &other.parent_style) && + self.local_name == other.local_name && + self.class == other.class && + self.link == other.link && + self.namespace == other.namespace && + self.common_style_affecting_attributes == other.common_style_affecting_attributes + } +} + +impl StyleSharingCandidate { + /// Attempts to create a style sharing candidate from this node. Returns + /// the style sharing candidate or `None` if this node is ineligible for + /// style sharing. + fn new<'le, E: TElement<'le>>(element: &E) -> Option<StyleSharingCandidate> { + let parent_element = match element.parent_element() { + None => return None, + Some(parent_element) => parent_element, + }; + + let style = unsafe { + match element.as_node().borrow_data_unchecked() { + None => return None, + Some(data_ref) => { + match (*data_ref).style { + None => return None, + Some(ref data) => (*data).clone(), + } + } + } + }; + let parent_style = unsafe { + match parent_element.as_node().borrow_data_unchecked() { + None => return None, + Some(parent_data_ref) => { + match (*parent_data_ref).style { + None => return None, + Some(ref data) => (*data).clone(), + } + } + } + }; + + if element.style_attribute().is_some() { + return None + } + + Some(StyleSharingCandidate { + style: style, + parent_style: parent_style, + local_name: element.get_local_name().clone(), + class: element.get_attr(&ns!(), &atom!("class")) + .map(|string| string.to_owned()), + link: element.is_link(), + namespace: (*element.get_namespace()).clone(), + common_style_affecting_attributes: + create_common_style_affecting_attributes_from_element::<'le, E>(&element) + }) + } + + pub fn can_share_style_with<'a, E: TElement<'a>>(&self, element: &E) -> bool { + if *element.get_local_name() != self.local_name { + return false + } + + // FIXME(pcwalton): Use `each_class` here instead of slow string comparison. + match (&self.class, element.get_attr(&ns!(), &atom!("class"))) { + (&None, Some(_)) | (&Some(_), None) => return false, + (&Some(ref this_class), Some(element_class)) if + element_class != &**this_class => { + return false + } + (&Some(_), Some(_)) | (&None, None) => {} + } + + if *element.get_namespace() != self.namespace { + return false + } + + let mut matching_rules = ForgetfulSink::new(); + element.synthesize_presentational_hints_for_legacy_attributes(&mut matching_rules); + if !matching_rules.is_empty() { + return false; + } + + // FIXME(pcwalton): It's probably faster to iterate over all the element's attributes and + // use the {common, rare}-style-affecting-attributes tables as lookup tables. + + for attribute_info in &common_style_affecting_attributes() { + match attribute_info.mode { + CommonStyleAffectingAttributeMode::IsPresent(flag) => { + if self.common_style_affecting_attributes.contains(flag) != + element.get_attr(&ns!(), &attribute_info.atom).is_some() { + return false + } + } + CommonStyleAffectingAttributeMode::IsEqual(target_value, flag) => { + match element.get_attr(&ns!(), &attribute_info.atom) { + Some(ref element_value) if self.common_style_affecting_attributes + .contains(flag) && + *element_value != target_value => { + return false + } + Some(_) if !self.common_style_affecting_attributes.contains(flag) => { + return false + } + None if self.common_style_affecting_attributes.contains(flag) => { + return false + } + _ => {} + } + } + } + } + + for attribute_name in &rare_style_affecting_attributes() { + if element.get_attr(&ns!(), attribute_name).is_some() { + return false + } + } + + if element.is_link() != self.link { + return false + } + + // TODO(pcwalton): We don't support visited links yet, but when we do there will need to + // be some logic here. + + true + } +} + +static STYLE_SHARING_CANDIDATE_CACHE_SIZE: usize = 40; + +impl StyleSharingCandidateCache { + pub fn new() -> StyleSharingCandidateCache { + StyleSharingCandidateCache { + cache: LRUCache::new(STYLE_SHARING_CANDIDATE_CACHE_SIZE), + } + } + + pub fn iter(&self) -> Iter<(StyleSharingCandidate, ())> { + self.cache.iter() + } + + pub fn insert_if_possible<'le, E: TElement<'le>>(&mut self, element: &E) { + match StyleSharingCandidate::new(element) { + None => {} + Some(candidate) => self.cache.insert(candidate, ()) + } + } + + pub fn touch(&mut self, index: usize) { + self.cache.touch(index) + } +} + + |