mirror of
https://github.com/servo/servo.git
synced 2025-08-04 21:20:23 +01:00
layout: Aggressively try to share styles with the last few nodes seen.
This roughly implements the same cache found in Blink. 50% improvement on the rainbow page, but at the cost of reducing parallel gains down to nothing. I added a new page, `perf-rainbow-hard.html`, which disables the optimizations in both Blink and Servo.
This commit is contained in:
parent
0b894113e9
commit
d3e56fa9cc
8 changed files with 17838 additions and 103 deletions
|
@ -6,8 +6,8 @@
|
||||||
|
|
||||||
use css::node_style::StyledNode;
|
use css::node_style::StyledNode;
|
||||||
use layout::extra::LayoutAuxMethods;
|
use layout::extra::LayoutAuxMethods;
|
||||||
use layout::util::LayoutDataAccess;
|
use layout::util::{LayoutDataAccess, LayoutDataWrapper};
|
||||||
use layout::wrapper::LayoutNode;
|
use layout::wrapper::{LayoutElement, LayoutNode};
|
||||||
|
|
||||||
use extra::arc::Arc;
|
use extra::arc::Arc;
|
||||||
use script::layout_interface::LayoutChan;
|
use script::layout_interface::LayoutChan;
|
||||||
|
@ -16,12 +16,16 @@ use servo_util::namespace::Null;
|
||||||
use servo_util::smallvec::{SmallVec, SmallVec0, SmallVec16};
|
use servo_util::smallvec::{SmallVec, SmallVec0, SmallVec16};
|
||||||
use std::cast;
|
use std::cast;
|
||||||
use std::to_bytes;
|
use std::to_bytes;
|
||||||
use style::{After, Before, ComputedValues, MatchedProperty, Stylist, TNode, cascade};
|
use std::vec::VecIterator;
|
||||||
|
use style::{After, Before, ComputedValues, MatchedProperty, Stylist, TElement, TNode, cascade};
|
||||||
|
|
||||||
pub struct ApplicableDeclarations {
|
pub struct ApplicableDeclarations {
|
||||||
normal: SmallVec16<MatchedProperty>,
|
normal: SmallVec16<MatchedProperty>,
|
||||||
before: SmallVec0<MatchedProperty>,
|
before: SmallVec0<MatchedProperty>,
|
||||||
after: SmallVec0<MatchedProperty>,
|
after: SmallVec0<MatchedProperty>,
|
||||||
|
|
||||||
|
/// Whether the `normal` declarations are shareable with other nodes.
|
||||||
|
normal_shareable: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ApplicableDeclarations {
|
impl ApplicableDeclarations {
|
||||||
|
@ -30,6 +34,7 @@ impl ApplicableDeclarations {
|
||||||
normal: SmallVec16::new(),
|
normal: SmallVec16::new(),
|
||||||
before: SmallVec0::new(),
|
before: SmallVec0::new(),
|
||||||
after: SmallVec0::new(),
|
after: SmallVec0::new(),
|
||||||
|
normal_shareable: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +42,7 @@ impl ApplicableDeclarations {
|
||||||
self.normal = SmallVec16::new();
|
self.normal = SmallVec16::new();
|
||||||
self.before = SmallVec0::new();
|
self.before = SmallVec0::new();
|
||||||
self.after = SmallVec0::new();
|
self.after = SmallVec0::new();
|
||||||
|
self.normal_shareable = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,19 +88,24 @@ impl<'a> ApplicableDeclarationsCacheQuery<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Workaround for lack of `ptr_eq` on Arcs...
|
||||||
|
#[inline]
|
||||||
|
fn arc_ptr_eq<T>(a: &Arc<T>, b: &Arc<T>) -> bool {
|
||||||
|
unsafe {
|
||||||
|
let a: uint = cast::transmute_copy(a);
|
||||||
|
let b: uint = cast::transmute_copy(b);
|
||||||
|
a == b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> Equiv<ApplicableDeclarationsCacheEntry> for ApplicableDeclarationsCacheQuery<'a> {
|
impl<'a> Equiv<ApplicableDeclarationsCacheEntry> for ApplicableDeclarationsCacheQuery<'a> {
|
||||||
fn equiv(&self, other: &ApplicableDeclarationsCacheEntry) -> bool {
|
fn equiv(&self, other: &ApplicableDeclarationsCacheEntry) -> bool {
|
||||||
if self.declarations.len() != other.declarations.len() {
|
if self.declarations.len() != other.declarations.len() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for (this, other) in self.declarations.iter().zip(other.declarations.iter()) {
|
for (this, other) in self.declarations.iter().zip(other.declarations.iter()) {
|
||||||
unsafe {
|
if !arc_ptr_eq(&this.declarations, &other.declarations) {
|
||||||
// Workaround for lack of `ptr_eq` on Arcs...
|
return false
|
||||||
let this: uint = cast::transmute_copy(this);
|
|
||||||
let other: uint = cast::transmute_copy(other);
|
|
||||||
if this != other {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
@ -139,6 +150,134 @@ impl ApplicableDeclarationsCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An LRU cache of the last few nodes seen, so that we can aggressively try to reuse their styles.
|
||||||
|
pub struct StyleSharingCandidateCache {
|
||||||
|
priv cache: LRUCache<StyleSharingCandidate,()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deriving(Clone)]
|
||||||
|
struct StyleSharingCandidate {
|
||||||
|
priv style: Arc<ComputedValues>,
|
||||||
|
priv parent_style: Arc<ComputedValues>,
|
||||||
|
|
||||||
|
// TODO(pcwalton): Intern.
|
||||||
|
priv local_name: ~str,
|
||||||
|
|
||||||
|
priv class: Option<~str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(node: &LayoutNode) -> Option<StyleSharingCandidate> {
|
||||||
|
let parent_node = match node.parent_node() {
|
||||||
|
None => return None,
|
||||||
|
Some(parent_node) => parent_node,
|
||||||
|
};
|
||||||
|
if !parent_node.is_element() {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
|
||||||
|
let style = unsafe {
|
||||||
|
match *node.borrow_layout_data_unchecked() {
|
||||||
|
None => return None,
|
||||||
|
Some(ref layout_data_ref) => {
|
||||||
|
match layout_data_ref.data.style {
|
||||||
|
None => return None,
|
||||||
|
Some(ref data) => (*data).clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let parent_style = unsafe {
|
||||||
|
match *parent_node.borrow_layout_data_unchecked() {
|
||||||
|
None => return None,
|
||||||
|
Some(ref parent_layout_data_ref) => {
|
||||||
|
match parent_layout_data_ref.data.style {
|
||||||
|
None => return None,
|
||||||
|
Some(ref data) => (*data).clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut style = Some(style);
|
||||||
|
let mut parent_style = Some(parent_style);
|
||||||
|
node.with_element(|element| {
|
||||||
|
if element.style_attribute().is_some() {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(StyleSharingCandidate {
|
||||||
|
style: style.take_unwrap(),
|
||||||
|
parent_style: parent_style.take_unwrap(),
|
||||||
|
local_name: element.get_local_name().to_str(),
|
||||||
|
class: element.get_attr(&Null, "class")
|
||||||
|
.map(|string| string.to_str()),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_share_style_with(&self, element: &LayoutElement) -> bool {
|
||||||
|
if element.get_local_name() != self.local_name {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
match (&self.class, element.get_attr(&Null, "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) => {}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static STYLE_SHARING_CANDIDATE_CACHE_SIZE: uint = 40;
|
||||||
|
|
||||||
|
impl StyleSharingCandidateCache {
|
||||||
|
pub fn new() -> StyleSharingCandidateCache {
|
||||||
|
StyleSharingCandidateCache {
|
||||||
|
cache: LRUCache::new(STYLE_SHARING_CANDIDATE_CACHE_SIZE),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter<'a>(&'a self) -> VecIterator<'a,(StyleSharingCandidate,())> {
|
||||||
|
self.cache.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_if_possible(&mut self, node: &LayoutNode) {
|
||||||
|
match StyleSharingCandidate::new(node) {
|
||||||
|
None => {}
|
||||||
|
Some(candidate) => self.cache.insert(candidate, ())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn touch(&mut self, index: uint) {
|
||||||
|
self.cache.touch(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The results of attempting to share a style.
|
||||||
|
pub enum StyleSharingResult<'ln> {
|
||||||
|
/// We didn't find anybody to share the style with. The boolean indicates whether the style
|
||||||
|
/// is shareable at all.
|
||||||
|
CannotShare(bool),
|
||||||
|
/// The node's style can be shared. The integer specifies the index in the LRU cache that was
|
||||||
|
/// hit.
|
||||||
|
StyleWasShared(uint),
|
||||||
|
}
|
||||||
|
|
||||||
pub trait MatchMethods {
|
pub trait MatchMethods {
|
||||||
/// Performs aux initialization, selector matching, and cascading sequentially.
|
/// Performs aux initialization, selector matching, and cascading sequentially.
|
||||||
fn match_and_cascade_subtree(&self,
|
fn match_and_cascade_subtree(&self,
|
||||||
|
@ -147,9 +286,22 @@ pub trait MatchMethods {
|
||||||
applicable_declarations: &mut ApplicableDeclarations,
|
applicable_declarations: &mut ApplicableDeclarations,
|
||||||
initial_values: &ComputedValues,
|
initial_values: &ComputedValues,
|
||||||
applicable_declarations_cache: &mut ApplicableDeclarationsCache,
|
applicable_declarations_cache: &mut ApplicableDeclarationsCache,
|
||||||
|
style_sharing_candidate_cache: &mut StyleSharingCandidateCache,
|
||||||
parent: Option<LayoutNode>);
|
parent: Option<LayoutNode>);
|
||||||
|
|
||||||
fn match_node(&self, stylist: &Stylist, applicable_declarations: &mut ApplicableDeclarations);
|
fn match_node(&self,
|
||||||
|
stylist: &Stylist,
|
||||||
|
applicable_declarations: &mut ApplicableDeclarations,
|
||||||
|
shareable: &mut bool);
|
||||||
|
|
||||||
|
/// Attempts to share a style with another node. This method is unsafe because it depends on
|
||||||
|
/// the `style_sharing_candidate_cache` having only live nodes in it, and we have no way to
|
||||||
|
/// guarantee that at the type system level yet.
|
||||||
|
unsafe fn share_style_if_possible(&self,
|
||||||
|
style_sharing_candidate_cache:
|
||||||
|
&mut StyleSharingCandidateCache,
|
||||||
|
parent: Option<LayoutNode>)
|
||||||
|
-> StyleSharingResult;
|
||||||
|
|
||||||
unsafe fn cascade_node(&self,
|
unsafe fn cascade_node(&self,
|
||||||
parent: Option<LayoutNode>,
|
parent: Option<LayoutNode>,
|
||||||
|
@ -165,7 +317,13 @@ trait PrivateMatchMethods {
|
||||||
style: &mut Option<Arc<ComputedValues>>,
|
style: &mut Option<Arc<ComputedValues>>,
|
||||||
initial_values: &ComputedValues,
|
initial_values: &ComputedValues,
|
||||||
applicable_declarations_cache: &mut
|
applicable_declarations_cache: &mut
|
||||||
ApplicableDeclarationsCache);
|
ApplicableDeclarationsCache,
|
||||||
|
shareable: bool);
|
||||||
|
|
||||||
|
fn share_style_with_candidate_if_possible(&self,
|
||||||
|
parent_node: Option<LayoutNode>,
|
||||||
|
candidate: &StyleSharingCandidate)
|
||||||
|
-> Option<Arc<ComputedValues>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ln> PrivateMatchMethods for LayoutNode<'ln> {
|
impl<'ln> PrivateMatchMethods for LayoutNode<'ln> {
|
||||||
|
@ -175,7 +333,8 @@ impl<'ln> PrivateMatchMethods for LayoutNode<'ln> {
|
||||||
style: &mut Option<Arc<ComputedValues>>,
|
style: &mut Option<Arc<ComputedValues>>,
|
||||||
initial_values: &ComputedValues,
|
initial_values: &ComputedValues,
|
||||||
applicable_declarations_cache: &mut
|
applicable_declarations_cache: &mut
|
||||||
ApplicableDeclarationsCache) {
|
ApplicableDeclarationsCache,
|
||||||
|
shareable: bool) {
|
||||||
let this_style;
|
let this_style;
|
||||||
let cacheable;
|
let cacheable;
|
||||||
match parent_style {
|
match parent_style {
|
||||||
|
@ -187,6 +346,7 @@ impl<'ln> PrivateMatchMethods for LayoutNode<'ln> {
|
||||||
Some(ref style) => cached_computed_values = Some(style.get()),
|
Some(ref style) => cached_computed_values = Some(style.get()),
|
||||||
}
|
}
|
||||||
let (the_style, is_cacheable) = cascade(applicable_declarations,
|
let (the_style, is_cacheable) = cascade(applicable_declarations,
|
||||||
|
shareable,
|
||||||
Some(parent_style.get()),
|
Some(parent_style.get()),
|
||||||
initial_values,
|
initial_values,
|
||||||
cached_computed_values);
|
cached_computed_values);
|
||||||
|
@ -195,6 +355,7 @@ impl<'ln> PrivateMatchMethods for LayoutNode<'ln> {
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let (the_style, is_cacheable) = cascade(applicable_declarations,
|
let (the_style, is_cacheable) = cascade(applicable_declarations,
|
||||||
|
shareable,
|
||||||
None,
|
None,
|
||||||
initial_values,
|
initial_values,
|
||||||
None);
|
None);
|
||||||
|
@ -210,12 +371,49 @@ impl<'ln> PrivateMatchMethods for LayoutNode<'ln> {
|
||||||
|
|
||||||
*style = Some(this_style);
|
*style = Some(this_style);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn share_style_with_candidate_if_possible(&self,
|
||||||
|
parent_node: Option<LayoutNode>,
|
||||||
|
candidate: &StyleSharingCandidate)
|
||||||
|
-> Option<Arc<ComputedValues>> {
|
||||||
|
assert!(self.is_element());
|
||||||
|
|
||||||
|
let parent_node = match parent_node {
|
||||||
|
Some(parent_node) if parent_node.is_element() => parent_node,
|
||||||
|
Some(_) | None => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let parent_layout_data: &Option<LayoutDataWrapper> = unsafe {
|
||||||
|
cast::transmute(parent_node.borrow_layout_data_unchecked())
|
||||||
|
};
|
||||||
|
match parent_layout_data {
|
||||||
|
&Some(ref parent_layout_data_ref) => {
|
||||||
|
// Check parent style.
|
||||||
|
let parent_style = parent_layout_data_ref.data.style.as_ref().unwrap();
|
||||||
|
if !arc_ptr_eq(parent_style, &candidate.parent_style) {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check tag names, classes, etc.
|
||||||
|
if !self.with_element(|element| candidate.can_share_style_with(element)) {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
|
||||||
|
return Some(candidate.style.clone())
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ln> MatchMethods for LayoutNode<'ln> {
|
impl<'ln> MatchMethods for LayoutNode<'ln> {
|
||||||
fn match_node(&self,
|
fn match_node(&self,
|
||||||
stylist: &Stylist,
|
stylist: &Stylist,
|
||||||
applicable_declarations: &mut ApplicableDeclarations) {
|
applicable_declarations: &mut ApplicableDeclarations,
|
||||||
|
shareable: &mut bool) {
|
||||||
let style_attribute = self.with_element(|element| {
|
let style_attribute = self.with_element(|element| {
|
||||||
match *element.style_attribute() {
|
match *element.style_attribute() {
|
||||||
None => None,
|
None => None,
|
||||||
|
@ -223,10 +421,11 @@ impl<'ln> MatchMethods for LayoutNode<'ln> {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
stylist.push_applicable_declarations(self,
|
applicable_declarations.normal_shareable =
|
||||||
style_attribute,
|
stylist.push_applicable_declarations(self,
|
||||||
None,
|
style_attribute,
|
||||||
&mut applicable_declarations.normal);
|
None,
|
||||||
|
&mut applicable_declarations.normal);
|
||||||
stylist.push_applicable_declarations(self,
|
stylist.push_applicable_declarations(self,
|
||||||
None,
|
None,
|
||||||
Some(Before),
|
Some(Before),
|
||||||
|
@ -235,6 +434,43 @@ impl<'ln> MatchMethods for LayoutNode<'ln> {
|
||||||
None,
|
None,
|
||||||
Some(After),
|
Some(After),
|
||||||
&mut applicable_declarations.after);
|
&mut applicable_declarations.after);
|
||||||
|
|
||||||
|
*shareable = applicable_declarations.normal_shareable
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn share_style_if_possible(&self,
|
||||||
|
style_sharing_candidate_cache:
|
||||||
|
&mut StyleSharingCandidateCache,
|
||||||
|
parent: Option<LayoutNode>)
|
||||||
|
-> StyleSharingResult {
|
||||||
|
if !self.is_element() {
|
||||||
|
return CannotShare(false)
|
||||||
|
}
|
||||||
|
let ok = self.with_element(|element| {
|
||||||
|
element.style_attribute().is_none() && element.get_attr(&Null, "id").is_none()
|
||||||
|
});
|
||||||
|
if !ok {
|
||||||
|
return CannotShare(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, &(ref candidate, ())) in style_sharing_candidate_cache.iter().enumerate() {
|
||||||
|
match self.share_style_with_candidate_if_possible(parent, candidate) {
|
||||||
|
Some(shared_style) => {
|
||||||
|
// Yay, cache hit. Share the style.
|
||||||
|
let mut layout_data_ref = self.mutate_layout_data();
|
||||||
|
match *layout_data_ref.get() {
|
||||||
|
None => fail!(),
|
||||||
|
Some(ref mut layout_data_ref) => {
|
||||||
|
layout_data_ref.data.style = Some(shared_style);
|
||||||
|
return StyleWasShared(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CannotShare(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn match_and_cascade_subtree(&self,
|
fn match_and_cascade_subtree(&self,
|
||||||
|
@ -243,21 +479,38 @@ impl<'ln> MatchMethods for LayoutNode<'ln> {
|
||||||
applicable_declarations: &mut ApplicableDeclarations,
|
applicable_declarations: &mut ApplicableDeclarations,
|
||||||
initial_values: &ComputedValues,
|
initial_values: &ComputedValues,
|
||||||
applicable_declarations_cache: &mut ApplicableDeclarationsCache,
|
applicable_declarations_cache: &mut ApplicableDeclarationsCache,
|
||||||
|
style_sharing_candidate_cache: &mut StyleSharingCandidateCache,
|
||||||
parent: Option<LayoutNode>) {
|
parent: Option<LayoutNode>) {
|
||||||
self.initialize_layout_data((*layout_chan).clone());
|
self.initialize_layout_data((*layout_chan).clone());
|
||||||
|
|
||||||
if self.is_element() {
|
// First, check to see whether we can share a style with someone.
|
||||||
self.match_node(stylist, applicable_declarations);
|
let sharing_result = unsafe {
|
||||||
}
|
self.share_style_if_possible(style_sharing_candidate_cache, parent)
|
||||||
|
};
|
||||||
|
|
||||||
unsafe {
|
// Otherwise, match and cascade selectors.
|
||||||
self.cascade_node(parent,
|
match sharing_result {
|
||||||
initial_values,
|
CannotShare(mut shareable) => {
|
||||||
applicable_declarations,
|
if self.is_element() {
|
||||||
applicable_declarations_cache)
|
self.match_node(stylist, applicable_declarations, &mut shareable)
|
||||||
}
|
}
|
||||||
|
|
||||||
applicable_declarations.clear();
|
unsafe {
|
||||||
|
self.cascade_node(parent,
|
||||||
|
initial_values,
|
||||||
|
applicable_declarations,
|
||||||
|
applicable_declarations_cache)
|
||||||
|
}
|
||||||
|
|
||||||
|
applicable_declarations.clear();
|
||||||
|
|
||||||
|
// Add ourselves to the LRU cache.
|
||||||
|
if shareable {
|
||||||
|
style_sharing_candidate_cache.insert_if_possible(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StyleWasShared(index) => style_sharing_candidate_cache.touch(index),
|
||||||
|
}
|
||||||
|
|
||||||
for kid in self.children() {
|
for kid in self.children() {
|
||||||
kid.match_and_cascade_subtree(stylist,
|
kid.match_and_cascade_subtree(stylist,
|
||||||
|
@ -265,6 +518,7 @@ impl<'ln> MatchMethods for LayoutNode<'ln> {
|
||||||
applicable_declarations,
|
applicable_declarations,
|
||||||
initial_values,
|
initial_values,
|
||||||
applicable_declarations_cache,
|
applicable_declarations_cache,
|
||||||
|
style_sharing_candidate_cache,
|
||||||
Some(*self))
|
Some(*self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -303,20 +557,23 @@ impl<'ln> MatchMethods for LayoutNode<'ln> {
|
||||||
applicable_declarations.normal.as_slice(),
|
applicable_declarations.normal.as_slice(),
|
||||||
&mut layout_data.data.style,
|
&mut layout_data.data.style,
|
||||||
initial_values,
|
initial_values,
|
||||||
applicable_declarations_cache);
|
applicable_declarations_cache,
|
||||||
|
applicable_declarations.normal_shareable);
|
||||||
if applicable_declarations.before.len() > 0 {
|
if applicable_declarations.before.len() > 0 {
|
||||||
self.cascade_node_pseudo_element(parent_style,
|
self.cascade_node_pseudo_element(parent_style,
|
||||||
applicable_declarations.before.as_slice(),
|
applicable_declarations.before.as_slice(),
|
||||||
&mut layout_data.data.before_style,
|
&mut layout_data.data.before_style,
|
||||||
initial_values,
|
initial_values,
|
||||||
applicable_declarations_cache);
|
applicable_declarations_cache,
|
||||||
|
false);
|
||||||
}
|
}
|
||||||
if applicable_declarations.after.len() > 0 {
|
if applicable_declarations.after.len() > 0 {
|
||||||
self.cascade_node_pseudo_element(parent_style,
|
self.cascade_node_pseudo_element(parent_style,
|
||||||
applicable_declarations.after.as_slice(),
|
applicable_declarations.after.as_slice(),
|
||||||
&mut layout_data.data.after_style,
|
&mut layout_data.data.after_style,
|
||||||
initial_values,
|
initial_values,
|
||||||
applicable_declarations_cache);
|
applicable_declarations_cache,
|
||||||
|
false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
//! Data needed by the layout task.
|
//! Data needed by the layout task.
|
||||||
|
|
||||||
use css::matching::ApplicableDeclarationsCache;
|
use css::matching::{ApplicableDeclarationsCache, StyleSharingCandidateCache};
|
||||||
use layout::flow::FlowLeafSet;
|
use layout::flow::FlowLeafSet;
|
||||||
use layout::util::OpaqueNode;
|
use layout::util::OpaqueNode;
|
||||||
use layout::wrapper::DomLeafSet;
|
use layout::wrapper::DomLeafSet;
|
||||||
|
@ -31,6 +31,10 @@ static mut FONT_CONTEXT: *mut FontContext = 0 as *mut FontContext;
|
||||||
static mut APPLICABLE_DECLARATIONS_CACHE: *mut ApplicableDeclarationsCache =
|
static mut APPLICABLE_DECLARATIONS_CACHE: *mut ApplicableDeclarationsCache =
|
||||||
0 as *mut ApplicableDeclarationsCache;
|
0 as *mut ApplicableDeclarationsCache;
|
||||||
|
|
||||||
|
#[thread_local]
|
||||||
|
static mut STYLE_SHARING_CANDIDATE_CACHE: *mut StyleSharingCandidateCache =
|
||||||
|
0 as *mut StyleSharingCandidateCache;
|
||||||
|
|
||||||
/// Data shared by all layout workers.
|
/// Data shared by all layout workers.
|
||||||
#[deriving(Clone)]
|
#[deriving(Clone)]
|
||||||
pub struct LayoutContext {
|
pub struct LayoutContext {
|
||||||
|
@ -109,5 +113,27 @@ impl LayoutContext {
|
||||||
cast::transmute(APPLICABLE_DECLARATIONS_CACHE)
|
cast::transmute(APPLICABLE_DECLARATIONS_CACHE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn style_sharing_candidate_cache<'a>(&'a self) -> &'a mut StyleSharingCandidateCache {
|
||||||
|
// 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 STYLE_SHARING_CANDIDATE_CACHE == ptr::mut_null() {
|
||||||
|
let cache = ~StyleSharingCandidateCache::new();
|
||||||
|
STYLE_SHARING_CANDIDATE_CACHE = cast::transmute(cache)
|
||||||
|
}
|
||||||
|
cast::transmute(STYLE_SHARING_CANDIDATE_CACHE)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
/// rendered.
|
/// rendered.
|
||||||
|
|
||||||
use css::matching::{ApplicableDeclarations, ApplicableDeclarationsCache, MatchMethods};
|
use css::matching::{ApplicableDeclarations, ApplicableDeclarationsCache, MatchMethods};
|
||||||
|
use css::matching::{StyleSharingCandidateCache};
|
||||||
use css::select::new_stylist;
|
use css::select::new_stylist;
|
||||||
use css::node_style::StyledNode;
|
use css::node_style::StyledNode;
|
||||||
use layout::construct::{FlowConstructionResult, FlowConstructor, NoConstructionResult};
|
use layout::construct::{FlowConstructionResult, FlowConstructor, NoConstructionResult};
|
||||||
|
@ -570,11 +571,14 @@ impl LayoutTask {
|
||||||
let mut applicable_declarations = ApplicableDeclarations::new();
|
let mut applicable_declarations = ApplicableDeclarations::new();
|
||||||
let mut applicable_declarations_cache =
|
let mut applicable_declarations_cache =
|
||||||
ApplicableDeclarationsCache::new();
|
ApplicableDeclarationsCache::new();
|
||||||
|
let mut style_sharing_candidate_cache =
|
||||||
|
StyleSharingCandidateCache::new();
|
||||||
node.match_and_cascade_subtree(self.stylist,
|
node.match_and_cascade_subtree(self.stylist,
|
||||||
&layout_ctx.layout_chan,
|
&layout_ctx.layout_chan,
|
||||||
&mut applicable_declarations,
|
&mut applicable_declarations,
|
||||||
layout_ctx.initial_css_values.get(),
|
layout_ctx.initial_css_values.get(),
|
||||||
&mut applicable_declarations_cache,
|
&mut applicable_declarations_cache,
|
||||||
|
&mut style_sharing_candidate_cache,
|
||||||
None)
|
None)
|
||||||
}
|
}
|
||||||
Some(ref mut traversal) => {
|
Some(ref mut traversal) => {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
//!
|
//!
|
||||||
//! This code is highly unsafe. Keep this file small and easy to audit.
|
//! This code is highly unsafe. Keep this file small and easy to audit.
|
||||||
|
|
||||||
use css::matching::{ApplicableDeclarations, MatchMethods};
|
use css::matching::{ApplicableDeclarations, CannotShare, MatchMethods, StyleWasShared};
|
||||||
use layout::context::LayoutContext;
|
use layout::context::LayoutContext;
|
||||||
use layout::extra::LayoutAuxMethods;
|
use layout::extra::LayoutAuxMethods;
|
||||||
use layout::flow::{Flow, FlowLeafSet, PostorderFlowTraversal};
|
use layout::flow::{Flow, FlowLeafSet, PostorderFlowTraversal};
|
||||||
|
@ -149,24 +149,42 @@ fn match_and_cascade_node(unsafe_layout_node: UnsafeLayoutNode,
|
||||||
// parser.
|
// parser.
|
||||||
node.initialize_layout_data(layout_context.layout_chan.clone());
|
node.initialize_layout_data(layout_context.layout_chan.clone());
|
||||||
|
|
||||||
let mut applicable_declarations = ApplicableDeclarations::new();
|
// Get the parent node.
|
||||||
|
|
||||||
if node.is_element() {
|
|
||||||
// Perform the CSS selector matching.
|
|
||||||
let stylist: &Stylist = cast::transmute(layout_context.stylist);
|
|
||||||
node.match_node(stylist, &mut applicable_declarations);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform the CSS cascade.
|
|
||||||
let parent_opt = if OpaqueNode::from_layout_node(&node) == layout_context.reflow_root {
|
let parent_opt = if OpaqueNode::from_layout_node(&node) == layout_context.reflow_root {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
node.parent_node()
|
node.parent_node()
|
||||||
};
|
};
|
||||||
node.cascade_node(parent_opt,
|
|
||||||
layout_context.initial_css_values.get(),
|
// First, check to see whether we can share a style with someone.
|
||||||
&applicable_declarations,
|
let style_sharing_candidate_cache = layout_context.style_sharing_candidate_cache();
|
||||||
layout_context.applicable_declarations_cache());
|
let sharing_result = node.share_style_if_possible(style_sharing_candidate_cache,
|
||||||
|
parent_opt);
|
||||||
|
|
||||||
|
// Otherwise, match and cascade selectors.
|
||||||
|
match sharing_result {
|
||||||
|
CannotShare(mut shareable) => {
|
||||||
|
let mut applicable_declarations = ApplicableDeclarations::new();
|
||||||
|
|
||||||
|
if node.is_element() {
|
||||||
|
// Perform the CSS selector matching.
|
||||||
|
let stylist: &Stylist = cast::transmute(layout_context.stylist);
|
||||||
|
node.match_node(stylist, &mut applicable_declarations, &mut shareable);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform the CSS cascade.
|
||||||
|
node.cascade_node(parent_opt,
|
||||||
|
layout_context.initial_css_values.get(),
|
||||||
|
&applicable_declarations,
|
||||||
|
layout_context.applicable_declarations_cache());
|
||||||
|
|
||||||
|
// Add ourselves to the LRU cache.
|
||||||
|
if shareable {
|
||||||
|
style_sharing_candidate_cache.insert_if_possible(&node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StyleWasShared(index) => style_sharing_candidate_cache.touch(index),
|
||||||
|
}
|
||||||
|
|
||||||
// Enqueue kids.
|
// Enqueue kids.
|
||||||
let mut child_count = 0;
|
let mut child_count = 0;
|
||||||
|
|
|
@ -1122,6 +1122,7 @@ pub struct ComputedValues {
|
||||||
% for style_struct in STYLE_STRUCTS:
|
% for style_struct in STYLE_STRUCTS:
|
||||||
${style_struct.name}: CowArc<style_structs::${style_struct.name}>,
|
${style_struct.name}: CowArc<style_structs::${style_struct.name}>,
|
||||||
% endfor
|
% endfor
|
||||||
|
shareable: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ComputedValues {
|
impl ComputedValues {
|
||||||
|
@ -1150,12 +1151,14 @@ pub fn initial_values() -> ComputedValues {
|
||||||
% endfor
|
% endfor
|
||||||
}),
|
}),
|
||||||
% endfor
|
% endfor
|
||||||
|
shareable: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fast path for the function below. Only computes new inherited styles.
|
/// Fast path for the function below. Only computes new inherited styles.
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
fn cascade_with_cached_declarations(applicable_declarations: &[Arc<~[PropertyDeclaration]>],
|
fn cascade_with_cached_declarations(applicable_declarations: &[MatchedProperty],
|
||||||
|
shareable: bool,
|
||||||
parent_style: &ComputedValues,
|
parent_style: &ComputedValues,
|
||||||
cached_style: &ComputedValues)
|
cached_style: &ComputedValues)
|
||||||
-> ComputedValues {
|
-> ComputedValues {
|
||||||
|
@ -1226,6 +1229,7 @@ fn cascade_with_cached_declarations(applicable_declarations: &[Arc<~[PropertyDec
|
||||||
% for style_struct in STYLE_STRUCTS:
|
% for style_struct in STYLE_STRUCTS:
|
||||||
${style_struct.name}: style_${style_struct.name},
|
${style_struct.name}: style_${style_struct.name},
|
||||||
% endfor
|
% endfor
|
||||||
|
shareable: shareable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1234,6 +1238,9 @@ fn cascade_with_cached_declarations(applicable_declarations: &[Arc<~[PropertyDec
|
||||||
///
|
///
|
||||||
/// * `applicable_declarations`: The list of CSS rules that matched.
|
/// * `applicable_declarations`: The list of CSS rules that matched.
|
||||||
///
|
///
|
||||||
|
/// * `shareable`: Whether the `ComputedValues` structure to be constructed should be considered
|
||||||
|
/// shareable.
|
||||||
|
///
|
||||||
/// * `parent_style`: The parent style, if applicable; if `None`, this is the root node.
|
/// * `parent_style`: The parent style, if applicable; if `None`, this is the root node.
|
||||||
///
|
///
|
||||||
/// * `initial_values`: The initial set of CSS values as defined by the specification.
|
/// * `initial_values`: The initial set of CSS values as defined by the specification.
|
||||||
|
@ -1245,6 +1252,7 @@ fn cascade_with_cached_declarations(applicable_declarations: &[Arc<~[PropertyDec
|
||||||
///
|
///
|
||||||
/// Returns the computed values and a boolean indicating whether the result is cacheable.
|
/// Returns the computed values and a boolean indicating whether the result is cacheable.
|
||||||
pub fn cascade(applicable_declarations: &[MatchedProperty],
|
pub fn cascade(applicable_declarations: &[MatchedProperty],
|
||||||
|
shareable: bool,
|
||||||
parent_style: Option< &ComputedValues >,
|
parent_style: Option< &ComputedValues >,
|
||||||
initial_values: &ComputedValues,
|
initial_values: &ComputedValues,
|
||||||
cached_style: Option< &ComputedValues >)
|
cached_style: Option< &ComputedValues >)
|
||||||
|
@ -1252,6 +1260,7 @@ pub fn cascade(applicable_declarations: &[MatchedProperty],
|
||||||
match (cached_style, parent_style) {
|
match (cached_style, parent_style) {
|
||||||
(Some(cached_style), Some(parent_style)) => {
|
(Some(cached_style), Some(parent_style)) => {
|
||||||
return (cascade_with_cached_declarations(applicable_declarations,
|
return (cascade_with_cached_declarations(applicable_declarations,
|
||||||
|
shareable,
|
||||||
parent_style,
|
parent_style,
|
||||||
cached_style), false)
|
cached_style), false)
|
||||||
}
|
}
|
||||||
|
@ -1360,6 +1369,7 @@ pub fn cascade(applicable_declarations: &[MatchedProperty],
|
||||||
% for style_struct in STYLE_STRUCTS:
|
% for style_struct in STYLE_STRUCTS:
|
||||||
${style_struct.name}: style_${style_struct.name},
|
${style_struct.name}: style_${style_struct.name},
|
||||||
% endfor
|
% endfor
|
||||||
|
shareable: shareable,
|
||||||
}, cacheable)
|
}, cacheable)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -108,7 +108,8 @@ impl SelectorMap {
|
||||||
V:SmallVec<MatchedProperty>>(
|
V:SmallVec<MatchedProperty>>(
|
||||||
&self,
|
&self,
|
||||||
node: &N,
|
node: &N,
|
||||||
matching_rules_list: &mut V) {
|
matching_rules_list: &mut V,
|
||||||
|
shareable: &mut bool) {
|
||||||
if self.empty {
|
if self.empty {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -121,7 +122,8 @@ impl SelectorMap {
|
||||||
SelectorMap::get_matching_rules_from_hash(node,
|
SelectorMap::get_matching_rules_from_hash(node,
|
||||||
&self.id_hash,
|
&self.id_hash,
|
||||||
id,
|
id,
|
||||||
matching_rules_list)
|
matching_rules_list,
|
||||||
|
shareable)
|
||||||
}
|
}
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
|
@ -132,7 +134,8 @@ impl SelectorMap {
|
||||||
SelectorMap::get_matching_rules_from_hash(node,
|
SelectorMap::get_matching_rules_from_hash(node,
|
||||||
&self.class_hash,
|
&self.class_hash,
|
||||||
class,
|
class,
|
||||||
matching_rules_list);
|
matching_rules_list,
|
||||||
|
shareable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {}
|
None => {}
|
||||||
|
@ -143,10 +146,13 @@ impl SelectorMap {
|
||||||
SelectorMap::get_matching_rules_from_hash_ignoring_case(node,
|
SelectorMap::get_matching_rules_from_hash_ignoring_case(node,
|
||||||
&self.element_hash,
|
&self.element_hash,
|
||||||
element.get_local_name(),
|
element.get_local_name(),
|
||||||
matching_rules_list);
|
matching_rules_list,
|
||||||
|
shareable);
|
||||||
|
|
||||||
SelectorMap::get_matching_rules(node,
|
SelectorMap::get_matching_rules(node,
|
||||||
self.universal_rules,
|
self.universal_rules,
|
||||||
matching_rules_list);
|
matching_rules_list,
|
||||||
|
shareable);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Sort only the rules we just added.
|
// Sort only the rules we just added.
|
||||||
|
@ -159,10 +165,11 @@ impl SelectorMap {
|
||||||
node: &N,
|
node: &N,
|
||||||
hash: &HashMap<~str,~[Rule]>,
|
hash: &HashMap<~str,~[Rule]>,
|
||||||
key: &str,
|
key: &str,
|
||||||
matching_rules: &mut V) {
|
matching_rules: &mut V,
|
||||||
|
shareable: &mut bool) {
|
||||||
match hash.find_equiv(&key) {
|
match hash.find_equiv(&key) {
|
||||||
Some(rules) => {
|
Some(rules) => {
|
||||||
SelectorMap::get_matching_rules(node, *rules, matching_rules)
|
SelectorMap::get_matching_rules(node, *rules, matching_rules, shareable)
|
||||||
}
|
}
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
|
@ -174,10 +181,11 @@ impl SelectorMap {
|
||||||
node: &N,
|
node: &N,
|
||||||
hash: &HashMap<~str,~[Rule]>,
|
hash: &HashMap<~str,~[Rule]>,
|
||||||
key: &str,
|
key: &str,
|
||||||
matching_rules: &mut V) {
|
matching_rules: &mut V,
|
||||||
|
shareable: &mut bool) {
|
||||||
match hash.find_equiv(&LowercaseAsciiString(key)) {
|
match hash.find_equiv(&LowercaseAsciiString(key)) {
|
||||||
Some(rules) => {
|
Some(rules) => {
|
||||||
SelectorMap::get_matching_rules(node, *rules, matching_rules)
|
SelectorMap::get_matching_rules(node, *rules, matching_rules, shareable)
|
||||||
}
|
}
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
|
@ -189,9 +197,10 @@ impl SelectorMap {
|
||||||
V:SmallVec<MatchedProperty>>(
|
V:SmallVec<MatchedProperty>>(
|
||||||
node: &N,
|
node: &N,
|
||||||
rules: &[Rule],
|
rules: &[Rule],
|
||||||
matching_rules: &mut V) {
|
matching_rules: &mut V,
|
||||||
|
shareable: &mut bool) {
|
||||||
for rule in rules.iter() {
|
for rule in rules.iter() {
|
||||||
if matches_compound_selector(rule.selector.get(), node) {
|
if matches_compound_selector(rule.selector.get(), node, shareable) {
|
||||||
// TODO(pradeep): Is the cloning inefficient?
|
// TODO(pradeep): Is the cloning inefficient?
|
||||||
matching_rules.push(rule.property.clone());
|
matching_rules.push(rule.property.clone());
|
||||||
}
|
}
|
||||||
|
@ -364,6 +373,10 @@ impl Stylist {
|
||||||
|
|
||||||
/// Returns the applicable CSS declarations for the given element. This corresponds to
|
/// Returns the applicable CSS declarations for the given element. This corresponds to
|
||||||
/// `ElementRuleCollector` in WebKit.
|
/// `ElementRuleCollector` in WebKit.
|
||||||
|
///
|
||||||
|
/// The returned boolean indicates whether the style is *shareable*; that is, whether the
|
||||||
|
/// matched selectors are simple enough to allow the matching logic to be reduced to the logic
|
||||||
|
/// in `css::matching::PrivateMatchMethods::candidate_element_allows_for_style_sharing`.
|
||||||
pub fn push_applicable_declarations<E:TElement,
|
pub fn push_applicable_declarations<E:TElement,
|
||||||
N:TNode<E>,
|
N:TNode<E>,
|
||||||
V:SmallVec<MatchedProperty>>(
|
V:SmallVec<MatchedProperty>>(
|
||||||
|
@ -371,7 +384,8 @@ impl Stylist {
|
||||||
element: &N,
|
element: &N,
|
||||||
style_attribute: Option<&PropertyDeclarationBlock>,
|
style_attribute: Option<&PropertyDeclarationBlock>,
|
||||||
pseudo_element: Option<PseudoElement>,
|
pseudo_element: Option<PseudoElement>,
|
||||||
applicable_declarations: &mut V) {
|
applicable_declarations: &mut V)
|
||||||
|
-> bool {
|
||||||
assert!(element.is_element());
|
assert!(element.is_element());
|
||||||
assert!(style_attribute.is_none() || pseudo_element.is_none(),
|
assert!(style_attribute.is_none() || pseudo_element.is_none(),
|
||||||
"Style attributes do not apply to pseudo-elements");
|
"Style attributes do not apply to pseudo-elements");
|
||||||
|
@ -382,27 +396,41 @@ impl Stylist {
|
||||||
Some(After) => &self.after_map,
|
Some(After) => &self.after_map,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut shareable = true;
|
||||||
|
|
||||||
// Step 1: Normal rules.
|
// Step 1: Normal rules.
|
||||||
map.user_agent.normal.get_all_matching_rules(element, applicable_declarations);
|
map.user_agent.normal.get_all_matching_rules(element,
|
||||||
map.user.normal.get_all_matching_rules(element, applicable_declarations);
|
applicable_declarations,
|
||||||
map.author.normal.get_all_matching_rules(element, applicable_declarations);
|
&mut shareable);
|
||||||
|
map.user.normal.get_all_matching_rules(element, applicable_declarations, &mut shareable);
|
||||||
|
map.author.normal.get_all_matching_rules(element, applicable_declarations, &mut shareable);
|
||||||
|
|
||||||
// Step 2: Normal style attributes.
|
// Step 2: Normal style attributes.
|
||||||
style_attribute.map(|sa| {
|
style_attribute.map(|sa| {
|
||||||
|
shareable = false;
|
||||||
applicable_declarations.push(MatchedProperty::from_declarations(sa.normal.clone()))
|
applicable_declarations.push(MatchedProperty::from_declarations(sa.normal.clone()))
|
||||||
});
|
});
|
||||||
|
|
||||||
// Step 3: Author-supplied `!important` rules.
|
// Step 3: Author-supplied `!important` rules.
|
||||||
map.author.important.get_all_matching_rules(element, applicable_declarations);
|
map.author.important.get_all_matching_rules(element,
|
||||||
|
applicable_declarations,
|
||||||
|
&mut shareable);
|
||||||
|
|
||||||
// Step 4: `!important` style attributes.
|
// Step 4: `!important` style attributes.
|
||||||
style_attribute.map(|sa| {
|
style_attribute.map(|sa| {
|
||||||
|
shareable = false;
|
||||||
applicable_declarations.push(MatchedProperty::from_declarations(sa.important.clone()))
|
applicable_declarations.push(MatchedProperty::from_declarations(sa.important.clone()))
|
||||||
});
|
});
|
||||||
|
|
||||||
// Step 5: User and UA `!important` rules.
|
// Step 5: User and UA `!important` rules.
|
||||||
map.user.important.get_all_matching_rules(element, applicable_declarations);
|
map.user.important.get_all_matching_rules(element,
|
||||||
map.user_agent.important.get_all_matching_rules(element, applicable_declarations);
|
applicable_declarations,
|
||||||
|
&mut shareable);
|
||||||
|
map.user_agent.important.get_all_matching_rules(element,
|
||||||
|
applicable_declarations,
|
||||||
|
&mut shareable);
|
||||||
|
|
||||||
|
shareable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -485,10 +513,20 @@ impl Ord for MatchedProperty {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn matches_compound_selector<E:TElement,N:TNode<E>>(selector: &CompoundSelector, element: &N)
|
/// Determines whether the given element matches the given single or compound selector.
|
||||||
|
///
|
||||||
|
/// NB: If you add support for any new kinds of selectors to this routine, be sure to set
|
||||||
|
/// `shareable` to false unless you are willing to update the style sharing logic. Otherwise things
|
||||||
|
/// will almost certainly break as nodes will start mistakenly sharing styles. (See the code in
|
||||||
|
/// `main/css/matching.rs`.)
|
||||||
|
fn matches_compound_selector<E:TElement,
|
||||||
|
N:TNode<E>>(
|
||||||
|
selector: &CompoundSelector,
|
||||||
|
element: &N,
|
||||||
|
shareable: &mut bool)
|
||||||
-> bool {
|
-> bool {
|
||||||
if !selector.simple_selectors.iter().all(|simple_selector| {
|
if !selector.simple_selectors.iter().all(|simple_selector| {
|
||||||
matches_simple_selector(simple_selector, element)
|
matches_simple_selector(simple_selector, element, shareable)
|
||||||
}) {
|
}) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -513,7 +551,7 @@ fn matches_compound_selector<E:TElement,N:TNode<E>>(selector: &CompoundSelector,
|
||||||
Some(next_node) => node = next_node,
|
Some(next_node) => node = next_node,
|
||||||
}
|
}
|
||||||
if node.is_element() {
|
if node.is_element() {
|
||||||
if matches_compound_selector(&**next_selector, &node) {
|
if matches_compound_selector(&**next_selector, &node, shareable) {
|
||||||
return true
|
return true
|
||||||
} else if just_one {
|
} else if just_one {
|
||||||
return false
|
return false
|
||||||
|
@ -524,8 +562,19 @@ fn matches_compound_selector<E:TElement,N:TNode<E>>(selector: &CompoundSelector,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Determines whether the given element matches the given single selector.
|
||||||
|
///
|
||||||
|
/// NB: If you add support for any new kinds of selectors to this routine, be sure to set
|
||||||
|
/// `shareable` to false unless you are willing to update the style sharing logic. Otherwise things
|
||||||
|
/// will almost certainly break as nodes will start mistakenly sharing styles. (See the code in
|
||||||
|
/// `main/css/matching.rs`.)
|
||||||
#[inline]
|
#[inline]
|
||||||
fn matches_simple_selector<E:TElement,N:TNode<E>>(selector: &SimpleSelector, element: &N) -> bool {
|
fn matches_simple_selector<E:TElement,
|
||||||
|
N:TNode<E>>(
|
||||||
|
selector: &SimpleSelector,
|
||||||
|
element: &N,
|
||||||
|
shareable: &mut bool)
|
||||||
|
-> bool {
|
||||||
match *selector {
|
match *selector {
|
||||||
// TODO: case-sensitivity depends on the document type
|
// TODO: case-sensitivity depends on the document type
|
||||||
// TODO: intern element names
|
// TODO: intern element names
|
||||||
|
@ -534,7 +583,9 @@ fn matches_simple_selector<E:TElement,N:TNode<E>>(selector: &SimpleSelector, ele
|
||||||
element.get_local_name().eq_ignore_ascii_case(name.as_slice())
|
element.get_local_name().eq_ignore_ascii_case(name.as_slice())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
NamespaceSelector(ref namespace) => {
|
NamespaceSelector(ref namespace) => {
|
||||||
|
*shareable = false;
|
||||||
element.with_element(|element: &E| {
|
element.with_element(|element: &E| {
|
||||||
element.get_namespace() == namespace
|
element.get_namespace() == namespace
|
||||||
})
|
})
|
||||||
|
@ -542,6 +593,7 @@ fn matches_simple_selector<E:TElement,N:TNode<E>>(selector: &SimpleSelector, ele
|
||||||
// TODO: case-sensitivity depends on the document type and quirks mode
|
// TODO: case-sensitivity depends on the document type and quirks mode
|
||||||
// TODO: cache and intern IDs on elements.
|
// TODO: cache and intern IDs on elements.
|
||||||
IDSelector(ref id) => {
|
IDSelector(ref id) => {
|
||||||
|
*shareable = false;
|
||||||
element.with_element(|element: &E| {
|
element.with_element(|element: &E| {
|
||||||
match element.get_attr(&namespace::Null, "id") {
|
match element.get_attr(&namespace::Null, "id") {
|
||||||
Some(attr) => str::eq_slice(attr, *id),
|
Some(attr) => str::eq_slice(attr, *id),
|
||||||
|
@ -549,7 +601,7 @@ fn matches_simple_selector<E:TElement,N:TNode<E>>(selector: &SimpleSelector, ele
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// TODO: cache and intern classe names on elements.
|
// TODO: cache and intern class names on elements.
|
||||||
ClassSelector(ref class) => {
|
ClassSelector(ref class) => {
|
||||||
element.with_element(|element: &E| {
|
element.with_element(|element: &E| {
|
||||||
match element.get_attr(&namespace::Null, "class") {
|
match element.get_attr(&namespace::Null, "class") {
|
||||||
|
@ -561,32 +613,57 @@ fn matches_simple_selector<E:TElement,N:TNode<E>>(selector: &SimpleSelector, ele
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
AttrExists(ref attr) => element.match_attr(attr, |_| true),
|
AttrExists(ref attr) => {
|
||||||
AttrEqual(ref attr, ref value) => element.match_attr(attr, |v| v == value.as_slice()),
|
*shareable = false;
|
||||||
AttrIncludes(ref attr, ref value) => element.match_attr(attr, |attr_value| {
|
element.match_attr(attr, |_| true)
|
||||||
attr_value.split(SELECTOR_WHITESPACE).any(|v| v == value.as_slice())
|
}
|
||||||
}),
|
AttrEqual(ref attr, ref value) => {
|
||||||
AttrDashMatch(ref attr, ref value, ref dashing_value)
|
if value.as_slice() != "DIR" {
|
||||||
=> element.match_attr(attr, |attr_value| {
|
// FIXME(pcwalton): Remove once we start actually supporting RTL text. This is in
|
||||||
attr_value == value.as_slice() || attr_value.starts_with(dashing_value.as_slice())
|
// here because the UA style otherwise disables all style sharing completely.
|
||||||
}),
|
*shareable = false
|
||||||
AttrPrefixMatch(ref attr, ref value) => element.match_attr(attr, |attr_value| {
|
}
|
||||||
attr_value.starts_with(value.as_slice())
|
element.match_attr(attr, |v| v == value.as_slice())
|
||||||
}),
|
}
|
||||||
AttrSubstringMatch(ref attr, ref value) => element.match_attr(attr, |attr_value| {
|
AttrIncludes(ref attr, ref value) => {
|
||||||
attr_value.contains(value.as_slice())
|
*shareable = false;
|
||||||
}),
|
element.match_attr(attr, |attr_value| {
|
||||||
AttrSuffixMatch(ref attr, ref value) => element.match_attr(attr, |attr_value| {
|
attr_value.split(SELECTOR_WHITESPACE).any(|v| v == value.as_slice())
|
||||||
attr_value.ends_with(value.as_slice())
|
})
|
||||||
}),
|
}
|
||||||
|
AttrDashMatch(ref attr, ref value, ref dashing_value) => {
|
||||||
|
*shareable = false;
|
||||||
|
element.match_attr(attr, |attr_value| {
|
||||||
|
attr_value == value.as_slice() || attr_value.starts_with(dashing_value.as_slice())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
AttrPrefixMatch(ref attr, ref value) => {
|
||||||
|
*shareable = false;
|
||||||
|
element.match_attr(attr, |attr_value| {
|
||||||
|
attr_value.starts_with(value.as_slice())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
AttrSubstringMatch(ref attr, ref value) => {
|
||||||
|
*shareable = false;
|
||||||
|
element.match_attr(attr, |attr_value| {
|
||||||
|
attr_value.contains(value.as_slice())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
AttrSuffixMatch(ref attr, ref value) => {
|
||||||
|
*shareable = false;
|
||||||
|
element.match_attr(attr, |attr_value| {
|
||||||
|
attr_value.ends_with(value.as_slice())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
AnyLink => {
|
AnyLink => {
|
||||||
|
*shareable = false;
|
||||||
element.with_element(|element: &E| {
|
element.with_element(|element: &E| {
|
||||||
element.get_link().is_some()
|
element.get_link().is_some()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Link => {
|
Link => {
|
||||||
|
*shareable = false;
|
||||||
element.with_element(|element: &E| {
|
element.with_element(|element: &E| {
|
||||||
match element.get_link() {
|
match element.get_link() {
|
||||||
Some(url) => !url_is_visited(url),
|
Some(url) => !url_is_visited(url),
|
||||||
|
@ -595,6 +672,7 @@ fn matches_simple_selector<E:TElement,N:TNode<E>>(selector: &SimpleSelector, ele
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Visited => {
|
Visited => {
|
||||||
|
*shareable = false;
|
||||||
element.with_element(|element: &E| {
|
element.with_element(|element: &E| {
|
||||||
match element.get_link() {
|
match element.get_link() {
|
||||||
Some(url) => url_is_visited(url),
|
Some(url) => url_is_visited(url),
|
||||||
|
@ -604,29 +682,63 @@ fn matches_simple_selector<E:TElement,N:TNode<E>>(selector: &SimpleSelector, ele
|
||||||
}
|
}
|
||||||
|
|
||||||
Hover => {
|
Hover => {
|
||||||
|
*shareable = false;
|
||||||
element.with_element(|element: &E| {
|
element.with_element(|element: &E| {
|
||||||
element.get_hover_state()
|
element.get_hover_state()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
FirstChild => matches_first_child(element),
|
FirstChild => {
|
||||||
LastChild => matches_last_child(element),
|
*shareable = false;
|
||||||
OnlyChild => matches_first_child(element) &&
|
matches_first_child(element)
|
||||||
matches_last_child(element),
|
}
|
||||||
|
LastChild => {
|
||||||
|
*shareable = false;
|
||||||
|
matches_last_child(element)
|
||||||
|
}
|
||||||
|
OnlyChild => {
|
||||||
|
*shareable = false;
|
||||||
|
matches_first_child(element) && matches_last_child(element)
|
||||||
|
}
|
||||||
|
|
||||||
Root => matches_root(element),
|
Root => {
|
||||||
|
*shareable = false;
|
||||||
|
matches_root(element)
|
||||||
|
}
|
||||||
|
|
||||||
NthChild(a, b) => matches_generic_nth_child(element, a, b, false, false),
|
NthChild(a, b) => {
|
||||||
NthLastChild(a, b) => matches_generic_nth_child(element, a, b, false, true),
|
*shareable = false;
|
||||||
NthOfType(a, b) => matches_generic_nth_child(element, a, b, true, false),
|
matches_generic_nth_child(element, a, b, false, false)
|
||||||
NthLastOfType(a, b) => matches_generic_nth_child(element, a, b, true, true),
|
}
|
||||||
|
NthLastChild(a, b) => {
|
||||||
|
*shareable = false;
|
||||||
|
matches_generic_nth_child(element, a, b, false, true)
|
||||||
|
}
|
||||||
|
NthOfType(a, b) => {
|
||||||
|
*shareable = false;
|
||||||
|
matches_generic_nth_child(element, a, b, true, false)
|
||||||
|
}
|
||||||
|
NthLastOfType(a, b) => {
|
||||||
|
*shareable = false;
|
||||||
|
matches_generic_nth_child(element, a, b, true, true)
|
||||||
|
}
|
||||||
|
|
||||||
FirstOfType => matches_generic_nth_child(element, 0, 1, true, false),
|
FirstOfType => {
|
||||||
LastOfType => matches_generic_nth_child(element, 0, 1, true, true),
|
*shareable = false;
|
||||||
OnlyOfType => matches_generic_nth_child(element, 0, 1, true, false) &&
|
matches_generic_nth_child(element, 0, 1, true, false)
|
||||||
matches_generic_nth_child(element, 0, 1, true, true),
|
}
|
||||||
|
LastOfType => {
|
||||||
|
*shareable = false;
|
||||||
|
matches_generic_nth_child(element, 0, 1, true, true)
|
||||||
|
}
|
||||||
|
OnlyOfType => {
|
||||||
|
*shareable = false;
|
||||||
|
matches_generic_nth_child(element, 0, 1, true, false) &&
|
||||||
|
matches_generic_nth_child(element, 0, 1, true, true)
|
||||||
|
}
|
||||||
|
|
||||||
Negation(ref negated) => {
|
Negation(ref negated) => {
|
||||||
!negated.iter().all(|s| matches_simple_selector(s, element))
|
*shareable = false;
|
||||||
|
!negated.iter().all(|s| matches_simple_selector(s, element, shareable))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,6 +128,7 @@ impl<K: Clone + Eq, V: Clone> LRUCache<K,V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn touch(&mut self, pos: uint) -> V {
|
pub fn touch(&mut self, pos: uint) -> V {
|
||||||
let last_index = self.entries.len() - 1;
|
let last_index = self.entries.len() - 1;
|
||||||
if pos != last_index {
|
if pos != last_index {
|
||||||
|
@ -136,6 +137,10 @@ impl<K: Clone + Eq, V: Clone> LRUCache<K,V> {
|
||||||
}
|
}
|
||||||
self.entries[last_index].second_ref().clone()
|
self.entries[last_index].second_ref().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn iter<'a>(&'a self) -> VecIterator<'a,(K,V)> {
|
||||||
|
self.entries.iter()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: Clone + Eq, V: Clone> Cache<K,V> for LRUCache<K,V> {
|
impl<K: Clone + Eq, V: Clone> Cache<K,V> for LRUCache<K,V> {
|
||||||
|
|
17303
src/test/html/perf-rainbow-hard.html
Normal file
17303
src/test/html/perf-rainbow-hard.html
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue