mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
layout: Cache applicable-declarations-to-computed-values mappings.
If the cache is hit, then we can only compute inherited properties. This is a WebKit optimization.
This commit is contained in:
parent
1678cc9a88
commit
0fa0940ce9
8 changed files with 453 additions and 78 deletions
|
@ -6,13 +6,16 @@
|
|||
|
||||
use css::node_style::StyledNode;
|
||||
use layout::extra::LayoutAuxMethods;
|
||||
use layout::incremental;
|
||||
use layout::util::LayoutDataAccess;
|
||||
use layout::wrapper::LayoutNode;
|
||||
|
||||
use extra::arc::Arc;
|
||||
use script::layout_interface::LayoutChan;
|
||||
use servo_util::cache::{Cache, LRUCache, SimpleHashCache};
|
||||
use servo_util::namespace::Null;
|
||||
use servo_util::smallvec::{SmallVec, SmallVec0, SmallVec16};
|
||||
use std::cast;
|
||||
use std::to_bytes;
|
||||
use style::{After, Before, ComputedValues, PropertyDeclaration, Stylist, TNode, cascade};
|
||||
|
||||
pub struct ApplicableDeclarations {
|
||||
|
@ -37,6 +40,108 @@ impl ApplicableDeclarations {
|
|||
}
|
||||
}
|
||||
|
||||
#[deriving(Clone)]
|
||||
struct ApplicableDeclarationsCacheEntry {
|
||||
declarations: SmallVec16<Arc<~[PropertyDeclaration]>>,
|
||||
}
|
||||
|
||||
impl ApplicableDeclarationsCacheEntry {
|
||||
fn new(slice: &[Arc<~[PropertyDeclaration]>]) -> ApplicableDeclarationsCacheEntry {
|
||||
let mut entry_declarations = SmallVec16::new();
|
||||
for declarations in slice.iter() {
|
||||
entry_declarations.push(declarations.clone());
|
||||
}
|
||||
ApplicableDeclarationsCacheEntry {
|
||||
declarations: entry_declarations,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for ApplicableDeclarationsCacheEntry {
|
||||
fn eq(&self, other: &ApplicableDeclarationsCacheEntry) -> bool {
|
||||
let this_as_query = ApplicableDeclarationsCacheQuery::new(self.declarations.as_slice());
|
||||
this_as_query.equiv(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl IterBytes for ApplicableDeclarationsCacheEntry {
|
||||
fn iter_bytes(&self, lsb0: bool, f: to_bytes::Cb) -> bool {
|
||||
ApplicableDeclarationsCacheQuery::new(self.declarations.as_slice()).iter_bytes(lsb0, f)
|
||||
}
|
||||
}
|
||||
|
||||
struct ApplicableDeclarationsCacheQuery<'a> {
|
||||
declarations: &'a [Arc<~[PropertyDeclaration]>],
|
||||
}
|
||||
|
||||
impl<'a> ApplicableDeclarationsCacheQuery<'a> {
|
||||
fn new(declarations: &'a [Arc<~[PropertyDeclaration]>])
|
||||
-> ApplicableDeclarationsCacheQuery<'a> {
|
||||
ApplicableDeclarationsCacheQuery {
|
||||
declarations: declarations,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Equiv<ApplicableDeclarationsCacheEntry> for ApplicableDeclarationsCacheQuery<'a> {
|
||||
fn equiv(&self, other: &ApplicableDeclarationsCacheEntry) -> bool {
|
||||
if self.declarations.len() != other.declarations.len() {
|
||||
return false
|
||||
}
|
||||
for (this, other) in self.declarations.iter().zip(other.declarations.iter()) {
|
||||
unsafe {
|
||||
// Workaround for lack of `ptr_eq` on Arcs...
|
||||
let this: uint = cast::transmute_copy(this);
|
||||
let other: uint = cast::transmute_copy(other);
|
||||
if this != other {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IterBytes for ApplicableDeclarationsCacheQuery<'a> {
|
||||
fn iter_bytes(&self, lsb0: bool, f: to_bytes::Cb) -> bool {
|
||||
let mut result = true;
|
||||
for declaration in self.declarations.iter() {
|
||||
let ptr: uint = unsafe {
|
||||
cast::transmute_copy(declaration)
|
||||
};
|
||||
result = ptr.iter_bytes(lsb0, |x| f(x));
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
static APPLICABLE_DECLARATIONS_CACHE_SIZE: uint = 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: &[Arc<~[PropertyDeclaration]>]) -> Option<Arc<ComputedValues>> {
|
||||
match self.cache.find_equiv(&ApplicableDeclarationsCacheQuery::new(declarations)) {
|
||||
None => None,
|
||||
Some(ref values) => Some((*values).clone()),
|
||||
}
|
||||
}
|
||||
|
||||
fn insert(&mut self,
|
||||
declarations: &[Arc<~[PropertyDeclaration]>],
|
||||
style: Arc<ComputedValues>) {
|
||||
drop(self.cache.insert(ApplicableDeclarationsCacheEntry::new(declarations), style))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait MatchMethods {
|
||||
/// Performs aux initialization, selector matching, and cascading sequentially.
|
||||
fn match_and_cascade_subtree(&self,
|
||||
|
@ -44,6 +149,7 @@ pub trait MatchMethods {
|
|||
layout_chan: &LayoutChan,
|
||||
applicable_declarations: &mut ApplicableDeclarations,
|
||||
initial_values: &ComputedValues,
|
||||
applicable_declarations_cache: &mut ApplicableDeclarationsCache,
|
||||
parent: Option<LayoutNode>);
|
||||
|
||||
fn match_node(&self, stylist: &Stylist, applicable_declarations: &mut ApplicableDeclarations);
|
||||
|
@ -51,11 +157,68 @@ pub trait MatchMethods {
|
|||
unsafe fn cascade_node(&self,
|
||||
parent: Option<LayoutNode>,
|
||||
initial_values: &ComputedValues,
|
||||
applicable_declarations: &ApplicableDeclarations);
|
||||
applicable_declarations: &ApplicableDeclarations,
|
||||
applicable_declarations_cache: &mut ApplicableDeclarationsCache);
|
||||
}
|
||||
|
||||
trait PrivateMatchMethods {
|
||||
fn cascade_node_pseudo_element(&self,
|
||||
parent_style: Option<&Arc<ComputedValues>>,
|
||||
applicable_declarations: &[Arc<~[PropertyDeclaration]>],
|
||||
style: &mut Option<Arc<ComputedValues>>,
|
||||
initial_values: &ComputedValues,
|
||||
applicable_declarations_cache: &mut
|
||||
ApplicableDeclarationsCache);
|
||||
}
|
||||
|
||||
impl<'ln> PrivateMatchMethods for LayoutNode<'ln> {
|
||||
fn cascade_node_pseudo_element(&self,
|
||||
parent_style: Option<&Arc<ComputedValues>>,
|
||||
applicable_declarations: &[Arc<~[PropertyDeclaration]>],
|
||||
style: &mut Option<Arc<ComputedValues>>,
|
||||
initial_values: &ComputedValues,
|
||||
applicable_declarations_cache: &mut
|
||||
ApplicableDeclarationsCache) {
|
||||
let this_style;
|
||||
let cacheable;
|
||||
match parent_style {
|
||||
Some(ref parent_style) => {
|
||||
let cached_computed_values;
|
||||
let cache_entry = applicable_declarations_cache.find(applicable_declarations);
|
||||
match cache_entry {
|
||||
None => cached_computed_values = None,
|
||||
Some(ref style) => cached_computed_values = Some(style.get()),
|
||||
}
|
||||
let (the_style, is_cacheable) = cascade(applicable_declarations,
|
||||
Some(parent_style.get()),
|
||||
initial_values,
|
||||
cached_computed_values);
|
||||
cacheable = is_cacheable;
|
||||
this_style = Arc::new(the_style);
|
||||
}
|
||||
None => {
|
||||
let (the_style, is_cacheable) = cascade(applicable_declarations,
|
||||
None,
|
||||
initial_values,
|
||||
None);
|
||||
cacheable = is_cacheable;
|
||||
this_style = Arc::new(the_style);
|
||||
}
|
||||
};
|
||||
|
||||
// Cache the resolved style if it was cacheable.
|
||||
if cacheable {
|
||||
applicable_declarations_cache.insert(applicable_declarations, this_style.clone());
|
||||
}
|
||||
|
||||
*style = Some(this_style);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ln> MatchMethods for LayoutNode<'ln> {
|
||||
fn match_node(&self, stylist: &Stylist, applicable_declarations: &mut ApplicableDeclarations) {
|
||||
fn match_node(&self,
|
||||
stylist: &Stylist,
|
||||
applicable_declarations: &mut ApplicableDeclarations) {
|
||||
let style_attribute = self.with_element(|element| {
|
||||
match *element.style_attribute() {
|
||||
None => None,
|
||||
|
@ -82,6 +245,7 @@ impl<'ln> MatchMethods for LayoutNode<'ln> {
|
|||
layout_chan: &LayoutChan,
|
||||
applicable_declarations: &mut ApplicableDeclarations,
|
||||
initial_values: &ComputedValues,
|
||||
applicable_declarations_cache: &mut ApplicableDeclarationsCache,
|
||||
parent: Option<LayoutNode>) {
|
||||
self.initialize_layout_data((*layout_chan).clone());
|
||||
|
||||
|
@ -90,7 +254,10 @@ impl<'ln> MatchMethods for LayoutNode<'ln> {
|
|||
}
|
||||
|
||||
unsafe {
|
||||
self.cascade_node(parent, initial_values, applicable_declarations)
|
||||
self.cascade_node(parent,
|
||||
initial_values,
|
||||
applicable_declarations,
|
||||
applicable_declarations_cache)
|
||||
}
|
||||
|
||||
applicable_declarations.clear();
|
||||
|
@ -100,6 +267,7 @@ impl<'ln> MatchMethods for LayoutNode<'ln> {
|
|||
layout_chan,
|
||||
applicable_declarations,
|
||||
initial_values,
|
||||
applicable_declarations_cache,
|
||||
Some(*self))
|
||||
}
|
||||
}
|
||||
|
@ -107,65 +275,53 @@ impl<'ln> MatchMethods for LayoutNode<'ln> {
|
|||
unsafe fn cascade_node(&self,
|
||||
parent: Option<LayoutNode>,
|
||||
initial_values: &ComputedValues,
|
||||
applicable_declarations: &ApplicableDeclarations) {
|
||||
macro_rules! cascade_node(
|
||||
($applicable_declarations: expr, $style: ident) => {{
|
||||
// Get our parent's style. This must be unsafe so that we don't touch the parent's
|
||||
// borrow flags.
|
||||
//
|
||||
// FIXME(pcwalton): Isolate this unsafety into the `wrapper` module to allow
|
||||
// enforced safe, race-free access to the parent style.
|
||||
let parent_style = match parent {
|
||||
None => None,
|
||||
Some(parent_node) => {
|
||||
let parent_layout_data = parent_node.borrow_layout_data_unchecked();
|
||||
match *parent_layout_data {
|
||||
None => fail!("no parent data?!"),
|
||||
Some(ref parent_layout_data) => {
|
||||
match parent_layout_data.data.style {
|
||||
None => fail!("parent hasn't been styled yet?!"),
|
||||
Some(ref style) => Some(style),
|
||||
}
|
||||
}
|
||||
applicable_declarations: &ApplicableDeclarations,
|
||||
applicable_declarations_cache: &mut ApplicableDeclarationsCache) {
|
||||
// Get our parent's style. This must be unsafe so that we don't touch the parent's
|
||||
// borrow flags.
|
||||
//
|
||||
// FIXME(pcwalton): Isolate this unsafety into the `wrapper` module to allow
|
||||
// enforced safe, race-free access to the parent style.
|
||||
let parent_style = match parent {
|
||||
None => None,
|
||||
Some(parent_node) => {
|
||||
let parent_layout_data = parent_node.borrow_layout_data_unchecked();
|
||||
match *parent_layout_data {
|
||||
None => fail!("no parent data?!"),
|
||||
Some(ref parent_layout_data) => {
|
||||
match parent_layout_data.data.style {
|
||||
None => fail!("parent hasn't been styled yet?!"),
|
||||
Some(ref style) => Some(style),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let computed_values = match parent_style {
|
||||
Some(ref style) => {
|
||||
Arc::new(cascade($applicable_declarations.as_slice(),
|
||||
Some(style.get()),
|
||||
initial_values))
|
||||
}
|
||||
None => Arc::new(cascade($applicable_declarations.as_slice(),
|
||||
None,
|
||||
initial_values)),
|
||||
};
|
||||
|
||||
let mut layout_data_ref = self.mutate_layout_data();
|
||||
match *layout_data_ref.get() {
|
||||
None => fail!("no layout data"),
|
||||
Some(ref mut layout_data) => {
|
||||
let style = &mut layout_data.data.$style;
|
||||
match *style {
|
||||
None => (),
|
||||
Some(ref previous_style) => {
|
||||
layout_data.data.restyle_damage = Some(incremental::compute_damage(
|
||||
previous_style.get(), computed_values.get()).to_int())
|
||||
}
|
||||
}
|
||||
*style = Some(computed_values)
|
||||
}
|
||||
}
|
||||
}}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
cascade_node!(applicable_declarations.normal, style);
|
||||
if applicable_declarations.before.len() > 0 {
|
||||
cascade_node!(applicable_declarations.before, before_style);
|
||||
}
|
||||
if applicable_declarations.after.len() > 0 {
|
||||
cascade_node!(applicable_declarations.after, after_style);
|
||||
let mut layout_data_ref = self.mutate_layout_data();
|
||||
match *layout_data_ref.get() {
|
||||
None => fail!("no layout data"),
|
||||
Some(ref mut layout_data) => {
|
||||
self.cascade_node_pseudo_element(parent_style,
|
||||
applicable_declarations.normal.as_slice(),
|
||||
&mut layout_data.data.style,
|
||||
initial_values,
|
||||
applicable_declarations_cache);
|
||||
if applicable_declarations.before.len() > 0 {
|
||||
self.cascade_node_pseudo_element(parent_style,
|
||||
applicable_declarations.before.as_slice(),
|
||||
&mut layout_data.data.before_style,
|
||||
initial_values,
|
||||
applicable_declarations_cache);
|
||||
}
|
||||
if applicable_declarations.after.len() > 0 {
|
||||
self.cascade_node_pseudo_element(parent_style,
|
||||
applicable_declarations.after.as_slice(),
|
||||
&mut layout_data.data.after_style,
|
||||
initial_values,
|
||||
applicable_declarations_cache);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,28 +4,33 @@
|
|||
|
||||
//! Data needed by the layout task.
|
||||
|
||||
use extra::arc::{Arc, MutexArc};
|
||||
use green::task::GreenTask;
|
||||
use css::matching::ApplicableDeclarationsCache;
|
||||
use layout::flow::FlowLeafSet;
|
||||
use layout::util::OpaqueNode;
|
||||
use layout::wrapper::DomLeafSet;
|
||||
|
||||
use extra::arc::{Arc, MutexArc};
|
||||
use geom::size::Size2D;
|
||||
use gfx::font_context::{FontContext, FontContextInfo};
|
||||
use green::task::GreenTask;
|
||||
use script::layout_interface::LayoutChan;
|
||||
use servo_msg::constellation_msg::ConstellationChan;
|
||||
use servo_net::local_image_cache::LocalImageCache;
|
||||
use servo_util::geometry::Au;
|
||||
use std::cast;
|
||||
use std::ptr;
|
||||
use std::rt::Runtime;
|
||||
use std::rt::local::Local;
|
||||
use std::rt::task::Task;
|
||||
|
||||
use geom::size::Size2D;
|
||||
use gfx::font_context::{FontContext, FontContextInfo};
|
||||
use script::layout_interface::LayoutChan;
|
||||
use servo_msg::constellation_msg::ConstellationChan;
|
||||
use servo_net::local_image_cache::LocalImageCache;
|
||||
use servo_util::geometry::Au;
|
||||
use style::{ComputedValues, Stylist};
|
||||
|
||||
#[thread_local]
|
||||
static mut FONT_CONTEXT: *mut FontContext = 0 as *mut FontContext;
|
||||
|
||||
#[thread_local]
|
||||
static mut APPLICABLE_DECLARATIONS_CACHE: *mut ApplicableDeclarationsCache =
|
||||
0 as *mut ApplicableDeclarationsCache;
|
||||
|
||||
/// Data shared by all layout workers.
|
||||
#[deriving(Clone)]
|
||||
pub struct LayoutContext {
|
||||
|
@ -82,5 +87,27 @@ impl LayoutContext {
|
|||
cast::transmute(FONT_CONTEXT)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn applicable_declarations_cache<'a>(&'a self) -> &'a mut ApplicableDeclarationsCache {
|
||||
// Sanity check.
|
||||
{
|
||||
let mut task = Local::borrow(None::<Task>);
|
||||
match task.get().maybe_take_runtime::<GreenTask>() {
|
||||
Some(green) => {
|
||||
task.get().put_runtime(green as ~Runtime);
|
||||
fail!("can't call this on a green task!")
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
if APPLICABLE_DECLARATIONS_CACHE == ptr::mut_null() {
|
||||
let cache = ~ApplicableDeclarationsCache::new();
|
||||
APPLICABLE_DECLARATIONS_CACHE = cast::transmute(cache)
|
||||
}
|
||||
cast::transmute(APPLICABLE_DECLARATIONS_CACHE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
//! The layout task. Performs layout on the DOM, builds display lists and sends them to be
|
||||
/// rendered.
|
||||
|
||||
use css::matching::{ApplicableDeclarations, MatchMethods};
|
||||
use css::matching::{ApplicableDeclarations, ApplicableDeclarationsCache, MatchMethods};
|
||||
use css::select::new_stylist;
|
||||
use css::node_style::StyledNode;
|
||||
use layout::construct::{FlowConstructionResult, FlowConstructor, NoConstructionResult};
|
||||
|
@ -568,10 +568,13 @@ impl LayoutTask {
|
|||
match self.parallel_traversal {
|
||||
None => {
|
||||
let mut applicable_declarations = ApplicableDeclarations::new();
|
||||
let mut applicable_declarations_cache =
|
||||
ApplicableDeclarationsCache::new();
|
||||
node.match_and_cascade_subtree(self.stylist,
|
||||
&layout_ctx.layout_chan,
|
||||
&mut applicable_declarations,
|
||||
layout_ctx.initial_css_values.get(),
|
||||
&mut applicable_declarations_cache,
|
||||
None)
|
||||
}
|
||||
Some(ref mut traversal) => {
|
||||
|
|
|
@ -165,7 +165,8 @@ fn match_and_cascade_node(unsafe_layout_node: UnsafeLayoutNode,
|
|||
};
|
||||
node.cascade_node(parent_opt,
|
||||
layout_context.initial_css_values.get(),
|
||||
&applicable_declarations);
|
||||
&applicable_declarations,
|
||||
layout_context.applicable_declarations_cache());
|
||||
|
||||
// Enqueue kids.
|
||||
let mut child_count = 0;
|
||||
|
|
|
@ -130,10 +130,12 @@ impl ElementMapping {
|
|||
/// Data that layout associates with a node.
|
||||
pub struct PrivateLayoutData {
|
||||
/// The results of CSS styling for this node.
|
||||
before_style: Option<Arc<ComputedValues>>,
|
||||
|
||||
style: Option<Arc<ComputedValues>>,
|
||||
|
||||
/// The results of CSS styling for this node's `before` pseudo-element, if any.
|
||||
before_style: Option<Arc<ComputedValues>>,
|
||||
|
||||
/// The results of CSS styling for this node's `after` pseudo-element, if any.
|
||||
after_style: Option<Arc<ComputedValues>>,
|
||||
|
||||
/// Description of how to account for recent style changes.
|
||||
|
|
|
@ -1152,6 +1152,82 @@ pub fn initial_values() -> ComputedValues {
|
|||
}
|
||||
}
|
||||
|
||||
/// Fast path for the function below. Only computes new inherited styles.
|
||||
#[allow(unused_mut)]
|
||||
fn cascade_with_cached_declarations(applicable_declarations: &[Arc<~[PropertyDeclaration]>],
|
||||
parent_style: &ComputedValues,
|
||||
cached_style: &ComputedValues)
|
||||
-> ComputedValues {
|
||||
% for style_struct in STYLE_STRUCTS:
|
||||
% if style_struct.inherited:
|
||||
let mut style_${style_struct.name} = parent_style.${style_struct.name}.clone();
|
||||
% else:
|
||||
let mut style_${style_struct.name} = cached_style.${style_struct.name}.clone();
|
||||
% endif
|
||||
% endfor
|
||||
|
||||
let mut context = computed::Context::new(&style_Color,
|
||||
&style_Font,
|
||||
&style_Box,
|
||||
&style_Border,
|
||||
false);
|
||||
|
||||
<%def name="apply_cached(priority)">
|
||||
for sub_list in applicable_declarations.iter() {
|
||||
for declaration in sub_list.get().iter() {
|
||||
match declaration {
|
||||
% for style_struct in STYLE_STRUCTS:
|
||||
% if style_struct.inherited:
|
||||
% for property in style_struct.longhands:
|
||||
% if (property.needed_for_context and needed_for_context) or not \
|
||||
needed_for_context:
|
||||
&${property.ident}_declaration(SpecifiedValue(ref value)) => {
|
||||
% if property.needed_for_context and needed_for_context:
|
||||
context.set_${property.ident}(computed_value)
|
||||
% elif not needed_for_context:
|
||||
// Overwrite earlier declarations.
|
||||
let computed_value =
|
||||
longhands::${property.ident}::to_computed_value(
|
||||
(*value).clone(),
|
||||
&context);
|
||||
style_${style_struct.name}.get_mut()
|
||||
.${property.ident} =
|
||||
computed_value
|
||||
% endif
|
||||
}
|
||||
&${property.ident}_declaration(CSSWideKeyword(Initial)) => {
|
||||
let computed_value =
|
||||
longhands::${property.ident}::get_initial_value();
|
||||
% if property.needed_for_context and needed_for_context:
|
||||
context.set_${property.ident}(computed_value)
|
||||
% elif not needed_for_context:
|
||||
// Overwrite earlier declarations.
|
||||
style_${style_struct.name}.get_mut()
|
||||
.${property.ident} =
|
||||
computed_value
|
||||
% endif
|
||||
}
|
||||
% endif
|
||||
% endfor
|
||||
% endif
|
||||
% endfor
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
</%def>
|
||||
|
||||
${apply_cached(True)}
|
||||
context.use_parent_font_size = false;
|
||||
${apply_cached(False)}
|
||||
|
||||
ComputedValues {
|
||||
% for style_struct in STYLE_STRUCTS:
|
||||
${style_struct.name}: style_${style_struct.name},
|
||||
% endfor
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs the CSS cascade, computing new styles for an element from its parent style and
|
||||
/// optionally a cached related style. The arguments are:
|
||||
///
|
||||
|
@ -1161,11 +1237,26 @@ pub fn initial_values() -> ComputedValues {
|
|||
///
|
||||
/// * `initial_values`: The initial set of CSS values as defined by the specification.
|
||||
///
|
||||
/// Returns the computed values.
|
||||
/// * `cached_style`: If present, cascading is short-circuited for everything but inherited
|
||||
/// values and these values are used instead. Obviously, you must be careful when supplying
|
||||
/// this that it is safe to only provide inherited declarations. If `parent_style` is `None`,
|
||||
/// this is ignored.
|
||||
///
|
||||
/// Returns the computed values and a boolean indicating whether the result is cacheable.
|
||||
pub fn cascade(applicable_declarations: &[Arc<~[PropertyDeclaration]>],
|
||||
parent_style: Option< &ComputedValues>,
|
||||
initial_values: &ComputedValues)
|
||||
-> ComputedValues {
|
||||
parent_style: Option< &ComputedValues >,
|
||||
initial_values: &ComputedValues,
|
||||
cached_style: Option< &ComputedValues >)
|
||||
-> (ComputedValues, bool) {
|
||||
match (cached_style, parent_style) {
|
||||
(Some(cached_style), Some(parent_style)) => {
|
||||
return (cascade_with_cached_declarations(applicable_declarations,
|
||||
parent_style,
|
||||
cached_style), false)
|
||||
}
|
||||
(_, _) => {}
|
||||
}
|
||||
|
||||
let is_root_element;
|
||||
% for style_struct in STYLE_STRUCTS:
|
||||
let mut style_${style_struct.name};
|
||||
|
@ -1195,6 +1286,7 @@ pub fn cascade(applicable_declarations: &[Arc<~[PropertyDeclaration]>],
|
|||
&style_Border,
|
||||
is_root_element);
|
||||
|
||||
let mut cacheable = true;
|
||||
<%def name="apply(needed_for_context)">
|
||||
for sub_list in applicable_declarations.iter() {
|
||||
for declaration in sub_list.get().iter() {
|
||||
|
@ -1231,6 +1323,7 @@ pub fn cascade(applicable_declarations: &[Arc<~[PropertyDeclaration]>],
|
|||
% if not needed_for_context:
|
||||
&${property.ident}_declaration(CSSWideKeyword(Inherit)) => {
|
||||
// This is a bit slow, but this is rare so it shouldn't matter.
|
||||
cacheable = false;
|
||||
match parent_style {
|
||||
None => {
|
||||
style_${style_struct.name}.get_mut()
|
||||
|
@ -1262,11 +1355,11 @@ pub fn cascade(applicable_declarations: &[Arc<~[PropertyDeclaration]>],
|
|||
context.use_parent_font_size = false;
|
||||
${apply(False)}
|
||||
|
||||
ComputedValues {
|
||||
(ComputedValues {
|
||||
% for style_struct in STYLE_STRUCTS:
|
||||
${style_struct.name}: style_${style_struct.name},
|
||||
% endfor
|
||||
}
|
||||
}, cacheable)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -3,6 +3,10 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::hashmap::HashMap;
|
||||
use std::rand::Rng;
|
||||
use std::rand;
|
||||
use std::vec::VecIterator;
|
||||
use std::vec;
|
||||
|
||||
pub trait Cache<K: Eq, V: Clone> {
|
||||
fn insert(&mut self, key: K, value: V);
|
||||
|
@ -165,6 +169,73 @@ impl<K: Clone + Eq, V: Clone> Cache<K,V> for LRUCache<K,V> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct SimpleHashCache<K,V> {
|
||||
entries: ~[Option<(K,V)>],
|
||||
k0: u64,
|
||||
k1: u64,
|
||||
}
|
||||
|
||||
impl<K:Clone+Eq+Hash,V:Clone> SimpleHashCache<K,V> {
|
||||
pub fn new(cache_size: uint) -> SimpleHashCache<K,V> {
|
||||
let mut r = rand::task_rng();
|
||||
SimpleHashCache {
|
||||
entries: vec::from_elem(cache_size, None),
|
||||
k0: r.gen(),
|
||||
k1: r.gen(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_bucket(&self, h: uint) -> uint {
|
||||
h % self.entries.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn bucket_for_key<Q:Hash>(&self, key: &Q) -> uint {
|
||||
self.to_bucket(key.hash_keyed(self.k0, self.k1) as uint)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn find_equiv<'a,Q:Hash+Equiv<K>>(&'a self, key: &Q) -> Option<&'a V> {
|
||||
let bucket_index = self.bucket_for_key(key);
|
||||
match self.entries[bucket_index] {
|
||||
Some((ref existing_key, ref value)) if key.equiv(existing_key) => Some(value),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K:Clone+Eq+Hash,V:Clone> Cache<K,V> for SimpleHashCache<K,V> {
|
||||
fn insert(&mut self, key: K, value: V) {
|
||||
let bucket_index = self.bucket_for_key(&key);
|
||||
self.entries[bucket_index] = Some((key, value))
|
||||
}
|
||||
|
||||
fn find(&mut self, key: &K) -> Option<V> {
|
||||
let bucket_index = self.bucket_for_key(key);
|
||||
match self.entries[bucket_index] {
|
||||
Some((ref existing_key, ref value)) if existing_key == key => Some((*value).clone()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn find_or_create(&mut self, key: &K, blk: |&K| -> V) -> V {
|
||||
match self.find(key) {
|
||||
Some(value) => return value,
|
||||
None => {}
|
||||
}
|
||||
let value = blk(key);
|
||||
self.insert((*key).clone(), value.clone());
|
||||
value
|
||||
}
|
||||
|
||||
fn evict_all(&mut self) {
|
||||
for slot in self.entries.mut_iter() {
|
||||
*slot = None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lru_cache() {
|
||||
let one = @"one";
|
||||
|
|
|
@ -324,6 +324,20 @@ macro_rules! def_small_vector_drop_impl(
|
|||
)
|
||||
)
|
||||
|
||||
macro_rules! def_small_vector_clone_impl(
|
||||
($name:ident) => (
|
||||
impl<T:Clone> Clone for $name<T> {
|
||||
fn clone(&self) -> $name<T> {
|
||||
let mut new_vector = $name::new();
|
||||
for element in self.iter() {
|
||||
new_vector.push((*element).clone())
|
||||
}
|
||||
new_vector
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
macro_rules! def_small_vector_impl(
|
||||
($name:ident, $size:expr) => (
|
||||
impl<T> $name<T> {
|
||||
|
@ -396,47 +410,55 @@ impl<T> SmallVec0<T> {
|
|||
}
|
||||
|
||||
def_small_vector_drop_impl!(SmallVec0, 0)
|
||||
def_small_vector_clone_impl!(SmallVec0)
|
||||
|
||||
def_small_vector!(SmallVec1, 1)
|
||||
def_small_vector_private_trait_impl!(SmallVec1, 1)
|
||||
def_small_vector_trait_impl!(SmallVec1, 1)
|
||||
def_small_vector_drop_impl!(SmallVec1, 1)
|
||||
def_small_vector_clone_impl!(SmallVec1)
|
||||
def_small_vector_impl!(SmallVec1, 1)
|
||||
|
||||
def_small_vector!(SmallVec2, 2)
|
||||
def_small_vector_private_trait_impl!(SmallVec2, 2)
|
||||
def_small_vector_trait_impl!(SmallVec2, 2)
|
||||
def_small_vector_drop_impl!(SmallVec2, 2)
|
||||
def_small_vector_clone_impl!(SmallVec2)
|
||||
def_small_vector_impl!(SmallVec2, 2)
|
||||
|
||||
def_small_vector!(SmallVec4, 4)
|
||||
def_small_vector_private_trait_impl!(SmallVec4, 4)
|
||||
def_small_vector_trait_impl!(SmallVec4, 4)
|
||||
def_small_vector_drop_impl!(SmallVec4, 4)
|
||||
def_small_vector_clone_impl!(SmallVec4)
|
||||
def_small_vector_impl!(SmallVec4, 4)
|
||||
|
||||
def_small_vector!(SmallVec8, 8)
|
||||
def_small_vector_private_trait_impl!(SmallVec8, 8)
|
||||
def_small_vector_trait_impl!(SmallVec8, 8)
|
||||
def_small_vector_drop_impl!(SmallVec8, 8)
|
||||
def_small_vector_clone_impl!(SmallVec8)
|
||||
def_small_vector_impl!(SmallVec8, 8)
|
||||
|
||||
def_small_vector!(SmallVec16, 16)
|
||||
def_small_vector_private_trait_impl!(SmallVec16, 16)
|
||||
def_small_vector_trait_impl!(SmallVec16, 16)
|
||||
def_small_vector_drop_impl!(SmallVec16, 16)
|
||||
def_small_vector_clone_impl!(SmallVec16)
|
||||
def_small_vector_impl!(SmallVec16, 16)
|
||||
|
||||
def_small_vector!(SmallVec24, 24)
|
||||
def_small_vector_private_trait_impl!(SmallVec24, 24)
|
||||
def_small_vector_trait_impl!(SmallVec24, 24)
|
||||
def_small_vector_drop_impl!(SmallVec24, 24)
|
||||
def_small_vector_clone_impl!(SmallVec24)
|
||||
def_small_vector_impl!(SmallVec24, 24)
|
||||
|
||||
def_small_vector!(SmallVec32, 32)
|
||||
def_small_vector_private_trait_impl!(SmallVec32, 32)
|
||||
def_small_vector_trait_impl!(SmallVec32, 32)
|
||||
def_small_vector_drop_impl!(SmallVec32, 32)
|
||||
def_small_vector_clone_impl!(SmallVec32)
|
||||
def_small_vector_impl!(SmallVec32, 32)
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue