aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/layout/animation.rs6
-rw-r--r--components/layout/block.rs21
-rw-r--r--components/layout/context.rs78
-rw-r--r--components/layout/css/matching.rs351
-rw-r--r--components/layout/layout_task.rs23
-rw-r--r--components/layout/traversal.rs16
-rw-r--r--components/script/layout_interface.rs37
-rw-r--r--components/style/animation.rs23
-rw-r--r--components/style/context.rs74
-rw-r--r--components/style/lib.rs2
-rw-r--r--components/style/matching.rs343
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)
+ }
+}
+
+