mirror of
https://github.com/servo/servo.git
synced 2025-07-23 07:13:52 +01:00
Auto merge of #9077 - bholley:split_layout_context_etc, r=SimonSapin
Split the style-related bits out of LayoutContext and hoist more stuff into style/ <!-- Reviewable:start --> [<img src="https://reviewable.io/review_button.png" height=40 alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/9077) <!-- Reviewable:end -->
This commit is contained in:
commit
3f407ea3d6
11 changed files with 518 additions and 456 deletions
|
@ -40,7 +40,7 @@ pub fn start_transitions_if_applicable(new_animations_sender: &Mutex<Sender<Anim
|
||||||
let start_time =
|
let start_time =
|
||||||
now + (animation_style.transition_delay.0.get_mod(i).seconds() as f64);
|
now + (animation_style.transition_delay.0.get_mod(i).seconds() as f64);
|
||||||
new_animations_sender.lock().unwrap().send(Animation {
|
new_animations_sender.lock().unwrap().send(Animation {
|
||||||
node: node.id(),
|
node: node,
|
||||||
property_animation: property_animation,
|
property_animation: property_animation,
|
||||||
start_time: start_time,
|
start_time: start_time,
|
||||||
end_time: start_time +
|
end_time: start_time +
|
||||||
|
@ -101,7 +101,7 @@ pub fn update_animation_state(constellation_chan: &ConstellationChan<Constellati
|
||||||
|
|
||||||
// Add new running animations.
|
// Add new running animations.
|
||||||
for new_running_animation in 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::Vacant(entry) => {
|
||||||
entry.insert(vec![new_running_animation]);
|
entry.insert(vec![new_running_animation]);
|
||||||
}
|
}
|
||||||
|
@ -126,7 +126,7 @@ pub fn recalc_style_for_animations(flow: &mut Flow,
|
||||||
animations: &HashMap<OpaqueNode, Vec<Animation>>) {
|
animations: &HashMap<OpaqueNode, Vec<Animation>>) {
|
||||||
let mut damage = RestyleDamage::empty();
|
let mut damage = RestyleDamage::empty();
|
||||||
flow.mutate_fragments(&mut |fragment| {
|
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 {
|
for animation in *animations {
|
||||||
update_style_for_animation(animation, &mut fragment.style, Some(&mut damage));
|
update_style_for_animation(animation, &mut fragment.style, Some(&mut damage));
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,7 @@ use std::fmt;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use style::computed_values::{border_collapse, box_sizing, display, float, overflow_x, overflow_y};
|
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::computed_values::{position, text_align, transform_style};
|
||||||
|
use style::context::StyleContext;
|
||||||
use style::properties::ComputedValues;
|
use style::properties::ComputedValues;
|
||||||
use style::values::computed::{LengthOrNone, LengthOrPercentageOrNone};
|
use style::values::computed::{LengthOrNone, LengthOrPercentageOrNone};
|
||||||
use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};
|
use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};
|
||||||
|
@ -890,7 +891,7 @@ impl BlockFlow {
|
||||||
if is_root {
|
if is_root {
|
||||||
let viewport_size =
|
let viewport_size =
|
||||||
LogicalSize::from_physical(self.fragment.style.writing_mode,
|
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)
|
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> {
|
pub fn explicit_block_containing_size(&self, layout_context: &LayoutContext) -> Option<Au> {
|
||||||
if self.is_root() || self.is_fixed() {
|
if self.is_root() || self.is_fixed() {
|
||||||
let viewport_size = LogicalSize::from_physical(self.fragment.style.writing_mode,
|
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)
|
Some(viewport_size.block)
|
||||||
} else if self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) &&
|
} else if self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) &&
|
||||||
self.base.block_container_explicit_block_size.is_none() {
|
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) {
|
fn calculate_absolute_block_size_and_margins(&mut self, layout_context: &LayoutContext) {
|
||||||
let opaque_self = OpaqueFlow::from_flow(self);
|
let opaque_self = OpaqueFlow::from_flow(self);
|
||||||
let containing_block_block_size =
|
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
|
// This is the stored content block-size value from assign-block-size
|
||||||
let content_block_size = self.fragment.border_box.size.block;
|
let content_block_size = self.fragment.border_box.size.block;
|
||||||
|
@ -1270,7 +1271,7 @@ impl BlockFlow {
|
||||||
|
|
||||||
// Calculate containing block inline size.
|
// Calculate containing block inline size.
|
||||||
let containing_block_size = if flags.contains(IS_ABSOLUTELY_POSITIONED) {
|
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 {
|
} else {
|
||||||
content_inline_size
|
content_inline_size
|
||||||
};
|
};
|
||||||
|
@ -1603,7 +1604,7 @@ impl Flow for BlockFlow {
|
||||||
debug!("Setting root position");
|
debug!("Setting root position");
|
||||||
self.base.position.start = LogicalPoint::zero(self.base.writing_mode);
|
self.base.position.start = LogicalPoint::zero(self.base.writing_mode);
|
||||||
self.base.block_container_inline_size = LogicalSize::from_physical(
|
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;
|
self.base.block_container_writing_mode = self.base.writing_mode;
|
||||||
|
|
||||||
// The root element is never impacted by floats.
|
// The root element is never impacted by floats.
|
||||||
|
@ -1862,10 +1863,10 @@ impl Flow for BlockFlow {
|
||||||
let visible_rect =
|
let visible_rect =
|
||||||
match layout_context.shared.visible_rects.get(&self.layer_id()) {
|
match layout_context.shared.visible_rects.get(&self.layer_id()) {
|
||||||
Some(visible_rect) => *visible_rect,
|
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,
|
visible_rect.inflate(viewport_size.width * DISPLAY_PORT_SIZE_FACTOR,
|
||||||
viewport_size.height * DISPLAY_PORT_SIZE_FACTOR)
|
viewport_size.height * DISPLAY_PORT_SIZE_FACTOR)
|
||||||
} else if is_stacking_context {
|
} else if is_stacking_context {
|
||||||
|
@ -2591,7 +2592,7 @@ impl ISizeAndMarginsComputer for AbsoluteNonReplaced {
|
||||||
layout_context: &LayoutContext)
|
layout_context: &LayoutContext)
|
||||||
-> Au {
|
-> Au {
|
||||||
let opaque_block = OpaqueFlow::from_flow(block);
|
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,
|
fn set_inline_position_of_flow_if_necessary(&self,
|
||||||
|
@ -2703,7 +2704,7 @@ impl ISizeAndMarginsComputer for AbsoluteReplaced {
|
||||||
-> MaybeAuto {
|
-> MaybeAuto {
|
||||||
let opaque_block = OpaqueFlow::from_flow(block);
|
let opaque_block = OpaqueFlow::from_flow(block);
|
||||||
let containing_block_inline_size =
|
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();
|
let fragment = block.fragment();
|
||||||
fragment.assign_replaced_inline_size_if_necessary(containing_block_inline_size);
|
fragment.assign_replaced_inline_size_if_necessary(containing_block_inline_size);
|
||||||
// For replaced absolute flow, the rest of the constraint solving will
|
// For replaced absolute flow, the rest of the constraint solving will
|
||||||
|
@ -2717,7 +2718,7 @@ impl ISizeAndMarginsComputer for AbsoluteReplaced {
|
||||||
layout_context: &LayoutContext)
|
layout_context: &LayoutContext)
|
||||||
-> Au {
|
-> Au {
|
||||||
let opaque_block = OpaqueFlow::from_flow(block);
|
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,
|
fn set_inline_position_of_flow_if_necessary(&self,
|
||||||
|
|
|
@ -8,34 +8,30 @@
|
||||||
|
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use canvas_traits::CanvasMsg;
|
use canvas_traits::CanvasMsg;
|
||||||
use css::matching::{ApplicableDeclarationsCache, StyleSharingCandidateCache};
|
use euclid::Rect;
|
||||||
use euclid::{Rect, Size2D};
|
|
||||||
use fnv::FnvHasher;
|
use fnv::FnvHasher;
|
||||||
use gfx::display_list::OpaqueNode;
|
|
||||||
use gfx::font_cache_task::FontCacheTask;
|
use gfx::font_cache_task::FontCacheTask;
|
||||||
use gfx::font_context::FontContext;
|
use gfx::font_context::FontContext;
|
||||||
use gfx_traits::LayerId;
|
use gfx_traits::LayerId;
|
||||||
use ipc_channel::ipc::{self, IpcSender};
|
use ipc_channel::ipc::{self, IpcSender};
|
||||||
use msg::ParseErrorReporter;
|
|
||||||
use net_traits::image::base::Image;
|
use net_traits::image::base::Image;
|
||||||
use net_traits::image_cache_task::{ImageCacheChan, ImageCacheTask, ImageResponse, ImageState};
|
use net_traits::image_cache_task::{ImageCacheChan, ImageCacheTask, ImageResponse, ImageState};
|
||||||
use net_traits::image_cache_task::{UsePlaceholder};
|
use net_traits::image_cache_task::{UsePlaceholder};
|
||||||
use script::layout_interface::{Animation, ReflowGoal};
|
|
||||||
use std::cell::{RefCell, RefMut};
|
use std::cell::{RefCell, RefMut};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::hash_state::DefaultState;
|
use std::collections::hash_state::DefaultState;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::mpsc::{Sender, channel};
|
use std::sync::mpsc::{Sender, channel};
|
||||||
use std::sync::{Arc, Mutex, RwLock};
|
use std::sync::{Arc, Mutex};
|
||||||
use style::selector_matching::Stylist;
|
use style::context::{LocalStyleContext, SharedStyleContext, StyleContext};
|
||||||
|
use style::matching::{ApplicableDeclarationsCache, StyleSharingCandidateCache};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use util::mem::HeapSizeOf;
|
use util::mem::HeapSizeOf;
|
||||||
use util::opts;
|
use util::opts;
|
||||||
|
|
||||||
struct LocalLayoutContext {
|
struct LocalLayoutContext {
|
||||||
|
style_context: LocalStyleContext,
|
||||||
font_context: RefCell<FontContext>,
|
font_context: RefCell<FontContext>,
|
||||||
applicable_declarations_cache: RefCell<ApplicableDeclarationsCache>,
|
|
||||||
style_sharing_candidate_cache: RefCell<StyleSharingCandidateCache>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HeapSizeOf for LocalLayoutContext {
|
impl HeapSizeOf for LocalLayoutContext {
|
||||||
|
@ -58,16 +54,18 @@ fn create_or_get_local_context(shared_layout_context: &SharedLayoutContext)
|
||||||
LOCAL_CONTEXT_KEY.with(|r| {
|
LOCAL_CONTEXT_KEY.with(|r| {
|
||||||
let mut r = r.borrow_mut();
|
let mut r = r.borrow_mut();
|
||||||
if let Some(context) = r.clone() {
|
if let Some(context) = r.clone() {
|
||||||
if shared_layout_context.screen_size_changed {
|
if shared_layout_context.style_context.screen_size_changed {
|
||||||
context.applicable_declarations_cache.borrow_mut().evict_all();
|
context.style_context.applicable_declarations_cache.borrow_mut().evict_all();
|
||||||
}
|
}
|
||||||
context
|
context
|
||||||
} else {
|
} else {
|
||||||
let font_cache_task = shared_layout_context.font_cache_task.lock().unwrap().clone();
|
let font_cache_task = shared_layout_context.font_cache_task.lock().unwrap().clone();
|
||||||
let context = Rc::new(LocalLayoutContext {
|
let context = Rc::new(LocalLayoutContext {
|
||||||
font_context: RefCell::new(FontContext::new(font_cache_task)),
|
style_context: LocalStyleContext {
|
||||||
applicable_declarations_cache: RefCell::new(ApplicableDeclarationsCache::new()),
|
applicable_declarations_cache: RefCell::new(ApplicableDeclarationsCache::new()),
|
||||||
style_sharing_candidate_cache: RefCell::new(StyleSharingCandidateCache::new()),
|
style_sharing_candidate_cache: RefCell::new(StyleSharingCandidateCache::new()),
|
||||||
|
},
|
||||||
|
font_context: RefCell::new(FontContext::new(font_cache_task)),
|
||||||
});
|
});
|
||||||
*r = Some(context.clone());
|
*r = Some(context.clone());
|
||||||
context
|
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.
|
/// Layout information shared among all workers. This must be thread-safe.
|
||||||
pub struct SharedLayoutContext {
|
pub struct SharedLayoutContext {
|
||||||
|
/// Bits shared by the layout and style system.
|
||||||
|
pub style_context: SharedStyleContext,
|
||||||
|
|
||||||
/// The shared image cache task.
|
/// The shared image cache task.
|
||||||
pub image_cache_task: ImageCacheTask,
|
pub image_cache_task: ImageCacheTask,
|
||||||
|
|
||||||
/// A channel for the image cache to send responses to.
|
/// A channel for the image cache to send responses to.
|
||||||
pub image_cache_sender: Mutex<ImageCacheChan>,
|
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.
|
/// Interface to the font cache task.
|
||||||
pub font_cache_task: Mutex<FontCacheTask>,
|
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.
|
/// The URL.
|
||||||
pub url: 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
|
/// 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>)>>,
|
pub canvas_layers_sender: Mutex<Sender<(LayerId, IpcSender<CanvasMsg>)>>,
|
||||||
|
|
||||||
/// The visible rects for each layer, as reported to us by the compositor.
|
/// The visible rects for each layer, as reported to us by the compositor.
|
||||||
pub visible_rects: Arc<HashMap<LayerId, Rect<Au>, DefaultState<FnvHasher>>>,
|
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> {
|
pub struct LayoutContext<'a> {
|
||||||
|
@ -138,6 +102,16 @@ pub struct LayoutContext<'a> {
|
||||||
cached_local_layout_context: Rc<LocalLayoutContext>,
|
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> {
|
impl<'a> LayoutContext<'a> {
|
||||||
pub fn new(shared_layout_context: &'a SharedLayoutContext) -> LayoutContext<'a> {
|
pub fn new(shared_layout_context: &'a SharedLayoutContext) -> LayoutContext<'a> {
|
||||||
|
|
||||||
|
@ -156,12 +130,12 @@ impl<'a> LayoutContext<'a> {
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn applicable_declarations_cache(&self) -> RefMut<ApplicableDeclarationsCache> {
|
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)]
|
#[inline(always)]
|
||||||
pub fn style_sharing_candidate_cache(&self) -> RefMut<StyleSharingCandidateCache> {
|
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)
|
pub fn get_or_request_image(&self, url: Url, use_placeholder: UsePlaceholder)
|
||||||
|
|
|
@ -13,347 +13,19 @@ use incremental::{self, RestyleDamage};
|
||||||
use msg::ParseErrorReporter;
|
use msg::ParseErrorReporter;
|
||||||
use script::layout_interface::Animation;
|
use script::layout_interface::Animation;
|
||||||
use selectors::bloom::BloomFilter;
|
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::parser::PseudoElement;
|
||||||
use selectors::{Element};
|
use selectors::{Element};
|
||||||
use smallvec::SmallVec;
|
|
||||||
use std::borrow::ToOwned;
|
|
||||||
use std::hash::{Hash, Hasher};
|
|
||||||
use std::mem::transmute;
|
use std::mem::transmute;
|
||||||
use std::slice::Iter;
|
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use string_cache::{Atom, Namespace};
|
|
||||||
use style::data::PrivateStyleData;
|
use style::data::PrivateStyleData;
|
||||||
use style::dom::{TElement, TNode};
|
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 style::selector_matching::{DeclarationBlock, Stylist};
|
||||||
use util::arc_ptr_eq;
|
use util::arc_ptr_eq;
|
||||||
use util::cache::{LRUCache, SimpleHashCache};
|
|
||||||
use util::opts;
|
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.
|
/// The results of attempting to share a style.
|
||||||
pub enum StyleSharingResult {
|
pub enum StyleSharingResult {
|
||||||
|
@ -454,22 +126,22 @@ impl<'ln, ConcreteNode> PrivateMatchMethods for ConcreteNode
|
||||||
None => None,
|
None => None,
|
||||||
Some(ref style) => Some(&**style),
|
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,
|
applicable_declarations,
|
||||||
shareable,
|
shareable,
|
||||||
Some(&***parent_style),
|
Some(&***parent_style),
|
||||||
cached_computed_values,
|
cached_computed_values,
|
||||||
layout_context.error_reporter.clone());
|
layout_context.style_context.error_reporter.clone());
|
||||||
cacheable = cacheable && is_cacheable;
|
cacheable = cacheable && is_cacheable;
|
||||||
this_style = the_style
|
this_style = the_style
|
||||||
}
|
}
|
||||||
None => {
|
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,
|
applicable_declarations,
|
||||||
shareable,
|
shareable,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
layout_context.error_reporter.clone());
|
layout_context.style_context.error_reporter.clone());
|
||||||
cacheable = cacheable && is_cacheable;
|
cacheable = cacheable && is_cacheable;
|
||||||
this_style = the_style
|
this_style = the_style
|
||||||
}
|
}
|
||||||
|
@ -516,7 +188,7 @@ impl<'ln, ConcreteNode> PrivateMatchMethods for ConcreteNode
|
||||||
let this_opaque = self.opaque();
|
let this_opaque = self.opaque();
|
||||||
let had_animations_to_expire;
|
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);
|
let animations_to_expire = all_expired_animations.get(&this_opaque);
|
||||||
had_animations_to_expire = animations_to_expire.is_some();
|
had_animations_to_expire = animations_to_expire.is_some();
|
||||||
if let Some(ref animations) = animations_to_expire {
|
if let Some(ref animations) = animations_to_expire {
|
||||||
|
@ -527,17 +199,18 @@ impl<'ln, ConcreteNode> PrivateMatchMethods for ConcreteNode
|
||||||
}
|
}
|
||||||
|
|
||||||
if had_animations_to_expire {
|
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.
|
// 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()
|
.read()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get(&this_opaque)
|
.get(&this_opaque)
|
||||||
.is_some();
|
.is_some();
|
||||||
if had_running_animations {
|
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() {
|
for running_animation in all_running_animations.get(&this_opaque).unwrap() {
|
||||||
animation::update_style_for_animation(running_animation, style, None);
|
animation::update_style_for_animation(running_animation, style, None);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ use app_units::Au;
|
||||||
use azure::azure::AzColor;
|
use azure::azure::AzColor;
|
||||||
use canvas_traits::CanvasMsg;
|
use canvas_traits::CanvasMsg;
|
||||||
use construct::ConstructionResult;
|
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 display_list_builder::ToGfxColor;
|
||||||
use euclid::Matrix4;
|
use euclid::Matrix4;
|
||||||
use euclid::point::Point2D;
|
use euclid::point::Point2D;
|
||||||
|
@ -64,6 +64,7 @@ use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::sync::mpsc::{channel, Sender, Receiver};
|
use std::sync::mpsc::{channel, Sender, Receiver};
|
||||||
use std::sync::{Arc, Mutex, MutexGuard, RwLock};
|
use std::sync::{Arc, Mutex, MutexGuard, RwLock};
|
||||||
use style::computed_values::{filter, mix_blend_mode};
|
use style::computed_values::{filter, mix_blend_mode};
|
||||||
|
use style::context::{SharedStyleContext, StylistWrapper};
|
||||||
use style::dom::{TDocument, TElement, TNode};
|
use style::dom::{TDocument, TElement, TNode};
|
||||||
use style::media_queries::{Device, MediaType};
|
use style::media_queries::{Device, MediaType};
|
||||||
use style::selector_matching::{Stylist, USER_OR_USER_AGENT_STYLESHEETS};
|
use style::selector_matching::{Stylist, USER_OR_USER_AGENT_STYLESHEETS};
|
||||||
|
@ -470,21 +471,23 @@ impl LayoutTask {
|
||||||
goal: ReflowGoal)
|
goal: ReflowGoal)
|
||||||
-> SharedLayoutContext {
|
-> SharedLayoutContext {
|
||||||
SharedLayoutContext {
|
SharedLayoutContext {
|
||||||
image_cache_task: self.image_cache_task.clone(),
|
style_context: SharedStyleContext {
|
||||||
image_cache_sender: Mutex::new(self.image_cache_sender.clone()),
|
|
||||||
viewport_size: self.viewport_size.clone(),
|
viewport_size: self.viewport_size.clone(),
|
||||||
screen_size_changed: screen_size_changed,
|
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),
|
stylist: StylistWrapper(&*rw_data.stylist),
|
||||||
url: (*url).clone(),
|
|
||||||
visible_rects: self.visible_rects.clone(),
|
|
||||||
generation: self.generation,
|
generation: self.generation,
|
||||||
new_animations_sender: Mutex::new(self.new_animations_sender.clone()),
|
|
||||||
goal: goal,
|
goal: goal,
|
||||||
|
new_animations_sender: Mutex::new(self.new_animations_sender.clone()),
|
||||||
running_animations: self.running_animations.clone(),
|
running_animations: self.running_animations.clone(),
|
||||||
expired_animations: self.expired_animations.clone(),
|
expired_animations: self.expired_animations.clone(),
|
||||||
error_reporter: self.error_reporter.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()),
|
||||||
|
font_cache_task: Mutex::new(self.font_cache_task.clone()),
|
||||||
|
canvas_layers_sender: Mutex::new(self.canvas_layers_sender.clone()),
|
||||||
|
url: (*url).clone(),
|
||||||
|
visible_rects: self.visible_rects.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
use construct::FlowConstructor;
|
use construct::FlowConstructor;
|
||||||
use context::LayoutContext;
|
use context::LayoutContext;
|
||||||
use css::matching::{ApplicableDeclarations, ElementMatchMethods, MatchMethods, StyleSharingResult};
|
use css::matching::{ElementMatchMethods, MatchMethods, StyleSharingResult};
|
||||||
use flow::{PostorderFlowTraversal, PreorderFlowTraversal};
|
use flow::{PostorderFlowTraversal, PreorderFlowTraversal};
|
||||||
use flow::{self, Flow};
|
use flow::{self, Flow};
|
||||||
use gfx::display_list::OpaqueNode;
|
use gfx::display_list::OpaqueNode;
|
||||||
|
@ -15,7 +15,9 @@ use script::layout_interface::ReflowGoal;
|
||||||
use selectors::bloom::BloomFilter;
|
use selectors::bloom::BloomFilter;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
use style::context::StyleContext;
|
||||||
use style::dom::UnsafeNode;
|
use style::dom::UnsafeNode;
|
||||||
|
use style::matching::ApplicableDeclarations;
|
||||||
use util::opts;
|
use util::opts;
|
||||||
use util::tid::tid;
|
use util::tid::tid;
|
||||||
use wrapper::{LayoutNode, ThreadSafeLayoutNode};
|
use wrapper::{LayoutNode, ThreadSafeLayoutNode};
|
||||||
|
@ -72,7 +74,7 @@ fn take_task_local_bloom_filter<'ln, N>(parent_node: Option<N>,
|
||||||
// Found cached bloom filter.
|
// Found cached bloom filter.
|
||||||
(Some(parent), Some((mut bloom_filter, old_node, old_generation))) => {
|
(Some(parent), Some((mut bloom_filter, old_node, old_generation))) => {
|
||||||
if old_node == parent.to_unsafe() &&
|
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.
|
// Hey, the cached parent is our parent! We can reuse the bloom filter.
|
||||||
debug!("[{}] Parent matches (={}). Reusing bloom filter.", tid(), old_node.0);
|
debug!("[{}] Parent matches (={}). Reusing bloom filter.", tid(), old_node.0);
|
||||||
} else {
|
} else {
|
||||||
|
@ -93,7 +95,7 @@ fn put_task_local_bloom_filter(bf: Box<BloomFilter>,
|
||||||
STYLE_BLOOM.with(move |style_bloom| {
|
STYLE_BLOOM.with(move |style_bloom| {
|
||||||
assert!(style_bloom.borrow().is_none(),
|
assert!(style_bloom.borrow().is_none(),
|
||||||
"Putting into a never-taken task-local bloom filter");
|
"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() {
|
let shareable_element = match node.as_element() {
|
||||||
Some(element) => {
|
Some(element) => {
|
||||||
// Perform the CSS selector matching.
|
// 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,
|
if element.match_element(stylist,
|
||||||
Some(&*bf),
|
Some(&*bf),
|
||||||
&mut applicable_declarations) {
|
&mut applicable_declarations) {
|
||||||
|
@ -216,7 +218,7 @@ impl<'a, 'ln, ConcreteLayoutNode> PreorderDomTraversal<'ln, ConcreteLayoutNode>
|
||||||
parent_opt,
|
parent_opt,
|
||||||
&applicable_declarations,
|
&applicable_declarations,
|
||||||
&mut self.layout_context.applicable_declarations_cache(),
|
&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.
|
// 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_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) {
|
match node.layout_parent_node(self.root) {
|
||||||
None => {
|
None => {
|
||||||
|
@ -401,6 +403,6 @@ impl<'a> PostorderFlowTraversal for BuildDisplayList<'a> {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn should_process(&self, _: &mut Flow) -> bool {
|
fn should_process(&self, _: &mut Flow) -> bool {
|
||||||
self.layout_context.shared.goal == ReflowGoal::ForDisplay
|
self.layout_context.shared_context().goal == ReflowGoal::ForDisplay
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@ use euclid::point::Point2D;
|
||||||
use euclid::rect::Rect;
|
use euclid::rect::Rect;
|
||||||
use gfx_traits::LayerId;
|
use gfx_traits::LayerId;
|
||||||
use ipc_channel::ipc::{IpcReceiver, IpcSender};
|
use ipc_channel::ipc::{IpcReceiver, IpcSender};
|
||||||
use libc::uintptr_t;
|
|
||||||
use msg::compositor_msg::Epoch;
|
use msg::compositor_msg::Epoch;
|
||||||
use msg::constellation_msg::{ConstellationChan, Failure, PipelineId};
|
use msg::constellation_msg::{ConstellationChan, Failure, PipelineId};
|
||||||
use msg::constellation_msg::{WindowSizeData};
|
use msg::constellation_msg::{WindowSizeData};
|
||||||
|
@ -25,11 +24,12 @@ use std::any::Any;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::mpsc::{Receiver, Sender, channel};
|
use std::sync::mpsc::{Receiver, Sender, channel};
|
||||||
use string_cache::Atom;
|
use string_cache::Atom;
|
||||||
use style::animation::PropertyAnimation;
|
|
||||||
use style::stylesheets::Stylesheet;
|
use style::stylesheets::Stylesheet;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use util::ipc::OptionalOpaqueIpcSender;
|
use util::ipc::OptionalOpaqueIpcSender;
|
||||||
|
|
||||||
|
pub use style::animation::Animation;
|
||||||
|
pub use style::context::ReflowGoal;
|
||||||
pub use dom::node::TrustedNodeAddress;
|
pub use dom::node::TrustedNodeAddress;
|
||||||
|
|
||||||
/// Asynchronous messages that script can send to layout.
|
/// 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.
|
/// Any query to perform with this reflow.
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub enum ReflowQueryType {
|
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 struct NewLayoutTaskInfo {
|
||||||
pub id: PipelineId,
|
pub id: PipelineId,
|
||||||
pub url: Url,
|
pub url: Url,
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use cssparser::{Color, RGBA};
|
use cssparser::{Color, RGBA};
|
||||||
|
use dom::OpaqueNode;
|
||||||
use euclid::point::Point2D;
|
use euclid::point::Point2D;
|
||||||
use properties::ComputedValues;
|
use properties::ComputedValues;
|
||||||
use properties::longhands::background_position::computed_value::T as BackgroundPosition;
|
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::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
|
||||||
use values::computed::{CalcLengthOrPercentage, Length, LengthOrPercentage, Time};
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct PropertyAnimation {
|
pub struct PropertyAnimation {
|
||||||
property: AnimatedProperty,
|
property: AnimatedProperty,
|
||||||
|
|
74
components/style/context.rs
Normal file
74
components/style/context.rs
Normal file
|
@ -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,
|
||||||
|
}
|
||||||
|
|
|
@ -43,10 +43,12 @@ extern crate util;
|
||||||
|
|
||||||
pub mod animation;
|
pub mod animation;
|
||||||
pub mod attr;
|
pub mod attr;
|
||||||
|
pub mod context;
|
||||||
mod custom_properties;
|
mod custom_properties;
|
||||||
pub mod data;
|
pub mod data;
|
||||||
pub mod dom;
|
pub mod dom;
|
||||||
pub mod font_face;
|
pub mod font_face;
|
||||||
|
pub mod matching;
|
||||||
pub mod media_queries;
|
pub mod media_queries;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
pub mod restyle_hints;
|
pub mod restyle_hints;
|
||||||
|
|
343
components/style/matching.rs
Normal file
343
components/style/matching.rs
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue