diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 3d7b0676fb8..957eadb53e4 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -97,6 +97,7 @@ use std::convert::TryFrom; use std::default::Default; use std::fmt; use std::rc::Rc; +use style::applicable_declarations::ApplicableDeclarationBlock; use style::attr::{AttrValue, LengthOrPercentageOrAuto}; use style::context::{QuirksMode, ReflowGoal}; use style::element_state::*; @@ -109,7 +110,6 @@ use style::selector_parser::extended_filtering; use style::shared_lock::{SharedRwLock, Locked}; use style::sink::Push; use style::stylearc::Arc; -use style::stylist::ApplicableDeclarationBlock; use style::thread_state; use style::values::{CSSFloat, Either}; use style::values::specified; diff --git a/components/script/layout_wrapper.rs b/components/script/layout_wrapper.rs index 15589619e17..916b9f5a329 100644 --- a/components/script/layout_wrapper.rs +++ b/components/script/layout_wrapper.rs @@ -61,6 +61,7 @@ use std::marker::PhantomData; use std::mem::transmute; use std::sync::atomic::Ordering; use style; +use style::applicable_declarations::ApplicableDeclarationBlock; use style::attr::AttrValue; use style::computed_values::display; use style::context::{QuirksMode, SharedStyleContext}; @@ -76,7 +77,6 @@ use style::shared_lock::{SharedRwLock as StyleSharedRwLock, Locked as StyleLocke use style::sink::Push; use style::str::is_whitespace; use style::stylearc::Arc; -use style::stylist::ApplicableDeclarationBlock; #[derive(Copy, Clone)] pub struct ServoLayoutNode<'a> { diff --git a/components/selectors/bloom.rs b/components/selectors/bloom.rs index b60fc1be0b8..0f6fabbaa10 100644 --- a/components/selectors/bloom.rs +++ b/components/selectors/bloom.rs @@ -7,10 +7,13 @@ use fnv::FnvHasher; use std::hash::{Hash, Hasher}; +// The top 12 bits of the 32-bit hash value are not used by the bloom filter. +// Consumers may rely on this to pack hashes more efficiently. +pub const BLOOM_HASH_MASK: u32 = 0x00ffffff; const KEY_SIZE: usize = 12; + const ARRAY_SIZE: usize = 1 << KEY_SIZE; const KEY_MASK: u32 = (1 << KEY_SIZE) - 1; -const KEY_SHIFT: usize = 16; /// A counting Bloom filter with 8-bit counters. For now we assume /// that having two hash functions is enough, but we may revisit that @@ -183,7 +186,7 @@ fn hash1(hash: u32) -> u32 { #[inline] fn hash2(hash: u32) -> u32 { - (hash >> KEY_SHIFT) & KEY_MASK + (hash >> KEY_SIZE) & KEY_MASK } #[test] diff --git a/components/selectors/matching.rs b/components/selectors/matching.rs index 9bb49cea13a..7cc4735b09c 100644 --- a/components/selectors/matching.rs +++ b/components/selectors/matching.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use attr::{ParsedAttrSelectorOperation, AttrSelectorOperation, NamespaceConstraint}; -use bloom::BloomFilter; +use bloom::{BLOOM_HASH_MASK, BloomFilter}; use parser::{AncestorHashes, Combinator, Component, LocalName}; use parser::{Selector, SelectorImpl, SelectorIter, SelectorList}; use std::borrow::Borrow; @@ -279,19 +279,30 @@ fn may_match(hashes: &AncestorHashes, -> bool where E: Element, { - // Check against the list of precomputed hashes. - for hash in hashes.0.iter() { - // If we hit the 0 sentinel hash, that means the rest are zero as well. - if *hash == 0 { - break; + // Check the first three hashes. Note that we can check for zero before + // masking off the high bits, since if any of the first three hashes is + // zero the fourth will be as well. We also take care to avoid the + // special-case complexity of the fourth hash until we actually reach it, + // because we usually don't. + // + // To be clear: this is all extremely hot. + for i in 0..3 { + let packed = hashes.packed_hashes[i]; + if packed == 0 { + // No more hashes left - unable to fast-reject. + return true; } - if !bf.might_contain_hash(*hash) { + if !bf.might_contain_hash(packed & BLOOM_HASH_MASK) { + // Hooray! We fast-rejected on this hash. return false; } } - true + // Now do the slighty-more-complex work of synthesizing the fourth hash, + // and check it against the filter if it exists. + let fourth = hashes.fourth_hash(); + fourth == 0 || bf.might_contain_hash(fourth) } /// Tracks whether we are currently looking for relevant links for a given diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs index 393070ffff2..612e89ba60e 100644 --- a/components/selectors/parser.rs +++ b/components/selectors/parser.rs @@ -4,6 +4,7 @@ use attr::{AttrSelectorWithNamespace, ParsedAttrSelectorOperation, AttrSelectorOperator}; use attr::{ParsedCaseSensitivity, SELECTOR_WHITESPACE, NamespaceConstraint}; +use bloom::BLOOM_HASH_MASK; use cssparser::{ParseError, BasicParseError}; use cssparser::{Token, Parser as CssParser, parse_nth, ToCss, serialize_identifier, CssStringWriter}; use precomputed_hash::PrecomputedHash; @@ -203,16 +204,25 @@ impl SelectorList { } } -/// Copied from Gecko, who copied it from WebKit. Note that increasing the -/// number of hashes here will adversely affect the cache hit when fast- -/// rejecting long lists of Rules with inline hashes. -const NUM_ANCESTOR_HASHES: usize = 4; - /// Ancestor hashes for the bloom filter. We precompute these and store them /// inline with selectors to optimize cache performance during matching. /// This matters a lot. +/// +/// We use 4 hashes, which is copied from Gecko, who copied it from WebKit. +/// Note that increasing the number of hashes here will adversely affect the +/// cache hit when fast-rejecting long lists of Rules with inline hashes. +/// +/// Because the bloom filter only uses the bottom 24 bits of the hash, we pack +/// the fourth hash into the upper bits of the first three hashes in order to +/// shrink Rule (whose size matters a lot). This scheme minimizes the runtime +/// overhead of the packing for the first three hashes (we just need to mask +/// off the upper bits) at the expense of making the fourth somewhat more +/// complicated to assemble, because we often bail out before checking all the +/// hashes. #[derive(Eq, PartialEq, Clone, Debug)] -pub struct AncestorHashes(pub [u32; NUM_ANCESTOR_HASHES]); +pub struct AncestorHashes { + pub packed_hashes: [u32; 3], +} impl AncestorHashes { pub fn new(s: &Selector) -> Self { @@ -220,20 +230,38 @@ impl AncestorHashes { } pub fn from_iter(iter: SelectorIter) -> Self { - let mut hashes = [0; NUM_ANCESTOR_HASHES]; // Compute ancestor hashes for the bloom filter. + let mut hashes = [0u32; 4]; let mut hash_iter = AncestorIter::new(iter) .map(|x| x.ancestor_hash()) .filter(|x| x.is_some()) .map(|x| x.unwrap()); - for i in 0..NUM_ANCESTOR_HASHES { + for i in 0..4 { hashes[i] = match hash_iter.next() { - Some(x) => x, + Some(x) => x & BLOOM_HASH_MASK, None => break, } } - AncestorHashes(hashes) + // Now, pack the fourth hash (if it exists) into the upper byte of each of + // the other three hashes. + let fourth = hashes[3]; + if fourth != 0 { + hashes[0] |= (fourth & 0x000000ff) << 24; + hashes[1] |= (fourth & 0x0000ff00) << 16; + hashes[2] |= (fourth & 0x00ff0000) << 8; + } + + AncestorHashes { + packed_hashes: [hashes[0], hashes[1], hashes[2]], + } + } + + /// Returns the fourth hash, reassembled from parts. + pub fn fourth_hash(&self) -> u32 { + ((self.packed_hashes[0] & 0xff000000) >> 24) | + ((self.packed_hashes[1] & 0xff000000) >> 16) | + ((self.packed_hashes[2] & 0xff000000) >> 8) } } diff --git a/components/style/applicable_declarations.rs b/components/style/applicable_declarations.rs new file mode 100644 index 00000000000..17cf13f7f4d --- /dev/null +++ b/components/style/applicable_declarations.rs @@ -0,0 +1,137 @@ +/* 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/. */ + +//! Applicable declarations management. + +use properties::PropertyDeclarationBlock; +use rule_tree::{CascadeLevel, StyleSource}; +use shared_lock::Locked; +use smallvec::SmallVec; +use std::fmt::{Debug, self}; +use std::mem; +use stylearc::Arc; + +/// List of applicable declarations. This is a transient structure that shuttles +/// declarations between selector matching and inserting into the rule tree, and +/// therefore we want to avoid heap-allocation where possible. +/// +/// In measurements on wikipedia, we pretty much never have more than 8 applicable +/// declarations, so we could consider making this 8 entries instead of 16. +/// However, it may depend a lot on workload, and stack space is cheap. +pub type ApplicableDeclarationList = SmallVec<[ApplicableDeclarationBlock; 16]>; + +/// Blink uses 18 bits to store source order, and does not check overflow [1]. +/// That's a limit that could be reached in realistic webpages, so we use +/// 24 bits and enforce defined behavior in the overflow case. +/// +/// Note that the value of 24 is also hard-coded into the level() accessor, +/// which does a byte-aligned load of the 4th byte. If you change this value +/// you'll need to change that as well. +/// +/// [1] https://cs.chromium.org/chromium/src/third_party/WebKit/Source/core/css/ +/// RuleSet.h?l=128&rcl=90140ab80b84d0f889abc253410f44ed54ae04f3 +const SOURCE_ORDER_BITS: usize = 24; +const SOURCE_ORDER_MASK: u32 = (1 << SOURCE_ORDER_BITS) - 1; +const SOURCE_ORDER_MAX: u32 = SOURCE_ORDER_MASK; + +/// Stores the source order of a block and the cascade level it belongs to. +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[derive(Copy, Clone, Eq, PartialEq)] +struct SourceOrderAndCascadeLevel(u32); + +impl SourceOrderAndCascadeLevel { + fn new(source_order: u32, cascade_level: CascadeLevel) -> SourceOrderAndCascadeLevel { + let mut bits = ::std::cmp::min(source_order, SOURCE_ORDER_MAX); + bits |= (cascade_level as u8 as u32) << SOURCE_ORDER_BITS; + SourceOrderAndCascadeLevel(bits) + } + + fn order(&self) -> u32 { + self.0 & SOURCE_ORDER_MASK + } + + fn level(&self) -> CascadeLevel { + unsafe { + // Transmute rather than shifting so that we're sure the compiler + // emits a simple byte-aligned load. + let as_bytes: [u8; 4] = mem::transmute(self.0); + CascadeLevel::from_byte(as_bytes[3]) + } + } +} + +impl Debug for SourceOrderAndCascadeLevel { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("SourceOrderAndCascadeLevel") + .field("order", &self.order()) + .field("level", &self.level()) + .finish() + } +} + +/// A property declaration together with its precedence among rules of equal +/// specificity so that we can sort them. +/// +/// This represents the declarations in a given declaration block for a given +/// importance. +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[derive(Debug, Clone, PartialEq)] +pub struct ApplicableDeclarationBlock { + /// The style source, either a style rule, or a property declaration block. + #[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")] + pub source: StyleSource, + /// The source order of the block, and the cascade level it belongs to. + order_and_level: SourceOrderAndCascadeLevel, + /// The specificity of the selector this block is represented by. + pub specificity: u32, +} + +impl ApplicableDeclarationBlock { + /// Constructs an applicable declaration block from a given property + /// declaration block and importance. + #[inline] + pub fn from_declarations(declarations: Arc>, + level: CascadeLevel) + -> Self { + ApplicableDeclarationBlock { + source: StyleSource::Declarations(declarations), + order_and_level: SourceOrderAndCascadeLevel::new(0, level), + specificity: 0, + } + } + + /// Constructs an applicable declaration block from the given components + #[inline] + pub fn new(source: StyleSource, + order: u32, + level: CascadeLevel, + specificity: u32) -> Self { + ApplicableDeclarationBlock { + source: source, + order_and_level: SourceOrderAndCascadeLevel::new(order, level), + specificity: specificity, + } + + } + + /// Returns the source order of the block. + #[inline] + pub fn source_order(&self) -> u32 { + self.order_and_level.order() + } + + /// Returns the cascade level of the block. + #[inline] + pub fn level(&self) -> CascadeLevel { + self.order_and_level.level() + } + + /// Convenience method to consume self and return the source alongside the + /// level. + #[inline] + pub fn order_and_level(self) -> (StyleSource, CascadeLevel) { + let level = self.level(); + (self.source, level) + } +} diff --git a/components/style/dom.rs b/components/style/dom.rs index aef401dc43e..bbb4b279bf3 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -8,6 +8,7 @@ #![deny(missing_docs)] use {Atom, Namespace, LocalName}; +use applicable_declarations::ApplicableDeclarationBlock; use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut}; #[cfg(feature = "gecko")] use context::UpdateAnimationsTasks; use data::ElementData; @@ -29,7 +30,6 @@ use std::fmt::Debug; use std::hash::Hash; use std::ops::Deref; use stylearc::Arc; -use stylist::ApplicableDeclarationBlock; use thread_state; pub use style_traits::UnsafeNode; diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index 0ceb2c82a1b..64068a875f6 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -15,6 +15,7 @@ //! the separation between the style system implementation and everything else. use app_units::Au; +use applicable_declarations::ApplicableDeclarationBlock; use atomic_refcell::AtomicRefCell; use context::{QuirksMode, SharedStyleContext, UpdateAnimationsTasks}; use data::ElementData; @@ -87,7 +88,6 @@ use std::ptr; use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace}; use stylearc::Arc; use stylesheets::UrlExtraData; -use stylist::ApplicableDeclarationBlock; /// A simple wrapper over a non-null Gecko node (`nsINode`) pointer. /// diff --git a/components/style/lib.rs b/components/style/lib.rs index 0cde26b37f0..66cde1211d3 100644 --- a/components/style/lib.rs +++ b/components/style/lib.rs @@ -91,6 +91,7 @@ extern crate unicode_segmentation; mod macros; pub mod animation; +pub mod applicable_declarations; #[allow(missing_docs)] // TODO. #[cfg(feature = "servo")] pub mod attr; pub mod bezier; diff --git a/components/style/matching.rs b/components/style/matching.rs index 00eaba9b63e..d93eec27026 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -7,6 +7,7 @@ #![allow(unsafe_code)] #![deny(missing_docs)] +use applicable_declarations::ApplicableDeclarationList; use cascade_info::CascadeInfo; use context::{SelectorFlagsMap, SharedStyleContext, StyleContext}; use data::{ComputedStyle, ElementData, RestyleData}; @@ -25,7 +26,7 @@ use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode, S use selectors::matching::{VisitedHandlingMode, AFFECTED_BY_PSEUDO_ELEMENTS}; use sharing::StyleSharingBehavior; use stylearc::Arc; -use stylist::{ApplicableDeclarationList, RuleInclusion}; +use stylist::RuleInclusion; /// The way a style should be inherited. enum InheritMode { diff --git a/components/style/rule_tree/mod.rs b/components/style/rule_tree/mod.rs index be563d2230d..c1a3b2b69d5 100644 --- a/components/style/rule_tree/mod.rs +++ b/components/style/rule_tree/mod.rs @@ -6,17 +6,18 @@ //! The rule tree. +use applicable_declarations::ApplicableDeclarationList; #[cfg(feature = "servo")] use heapsize::HeapSizeOf; use properties::{AnimationRules, Importance, LonghandIdSet, PropertyDeclarationBlock}; use shared_lock::{Locked, StylesheetGuards, SharedRwLockReadGuard}; use smallvec::SmallVec; use std::io::{self, Write}; +use std::mem; use std::ptr; use std::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; use stylearc::{Arc, NonZeroPtrMut}; use stylesheets::StyleRule; -use stylist::ApplicableDeclarationList; use thread_state; /// The rule tree, the structure servo uses to preserve the results of selector @@ -229,7 +230,7 @@ impl RuleTree { guards: &StylesheetGuards) -> StrongRuleNode { - let rules = applicable_declarations.drain().map(|d| (d.source, d.level)); + let rules = applicable_declarations.drain().map(|d| d.order_and_level()); let rule_node = self.insert_ordered_rules_with_important(rules, guards); rule_node } @@ -425,10 +426,18 @@ pub enum CascadeLevel { /// User-agent important rules. UAImportant, /// Transitions + /// + /// NB: If this changes from being last, change from_byte below. Transitions, } impl CascadeLevel { + /// Converts a raw byte to a CascadeLevel. + pub unsafe fn from_byte(byte: u8) -> Self { + debug_assert!(byte <= CascadeLevel::Transitions as u8); + mem::transmute(byte) + } + /// Select a lock guard for this level pub fn guard<'a>(&self, guards: &'a StylesheetGuards<'a>) -> &'a SharedRwLockReadGuard<'a> { match *self { diff --git a/components/style/selector_map.rs b/components/style/selector_map.rs index a65b664ad38..6a5733877a2 100644 --- a/components/style/selector_map.rs +++ b/components/style/selector_map.rs @@ -6,6 +6,7 @@ //! name, ids and hash. use {Atom, LocalName}; +use applicable_declarations::ApplicableDeclarationBlock; use dom::TElement; use fnv::FnvHashMap; use pdqsort::sort_by; @@ -18,7 +19,7 @@ use smallvec::VecLike; use std::borrow::Borrow; use std::collections::HashMap; use std::hash::Hash; -use stylist::{ApplicableDeclarationBlock, Rule}; +use stylist::Rule; /// A trait to abstract over a given selector map entry. pub trait SelectorMapEntry : Sized + Clone { @@ -163,7 +164,7 @@ impl SelectorMap { // Sort only the rules we just added. sort_by_key(&mut matching_rules_list[init_len..], - |block| (block.specificity, block.source_order)); + |block| (block.specificity, block.source_order())); } /// Check whether we have rules for the given id @@ -190,7 +191,7 @@ impl SelectorMap { } sort_by_key(&mut rules_list, - |block| (block.specificity, block.source_order)); + |block| (block.specificity, block.source_order())); rules_list } diff --git a/components/style/sharing/mod.rs b/components/style/sharing/mod.rs index 3c17f576156..5971eb34367 100644 --- a/components/style/sharing/mod.rs +++ b/components/style/sharing/mod.rs @@ -65,6 +65,7 @@ //! elements makes sense. use Atom; +use applicable_declarations::ApplicableDeclarationBlock; use bit_vec::BitVec; use bloom::StyleBloom; use cache::{LRUCache, LRUCacheMutIterator}; @@ -78,7 +79,7 @@ use selectors::matching::{ElementSelectorFlags, VisitedHandlingMode, StyleRelati use smallvec::SmallVec; use std::mem; use std::ops::Deref; -use stylist::{ApplicableDeclarationBlock, Stylist}; +use stylist::Stylist; mod checks; diff --git a/components/style/stylist.rs b/components/style/stylist.rs index 89edcf0e86a..91de0ad3487 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -5,6 +5,7 @@ //! Selector matching. use {Atom, LocalName, Namespace}; +use applicable_declarations::{ApplicableDeclarationBlock, ApplicableDeclarationList}; use bit_vec::BitVec; use context::{QuirksMode, SharedStyleContext}; use data::ComputedStyle; @@ -33,7 +34,8 @@ use selectors::parser::{SelectorIter, SelectorMethods}; use selectors::visitor::SelectorVisitor; use shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards}; use sink::Push; -use smallvec::{SmallVec, VecLike}; +use smallvec::VecLike; +use std::fmt::Debug; #[cfg(feature = "servo")] use std::marker::PhantomData; use style_traits::viewport::ViewportConstraints; @@ -48,15 +50,6 @@ use thread_state; pub use ::fnv::FnvHashMap; -/// List of applicable declaration. This is a transient structure that shuttles -/// declarations between selector matching and inserting into the rule tree, and -/// therefore we want to avoid heap-allocation where possible. -/// -/// In measurements on wikipedia, we pretty much never have more than 8 applicable -/// declarations, so we could consider making this 8 entries instead of 16. -/// However, it may depend a lot on workload, and stack space is cheap. -pub type ApplicableDeclarationList = SmallVec<[ApplicableDeclarationBlock; 16]>; - /// This structure holds all the selectors and device characteristics /// for a given document. The selectors are converted into `Rule`s /// (defined in rust-selectors), and introduced in a `SelectorMap` @@ -121,7 +114,7 @@ pub struct Stylist { /// A monotonically increasing counter to represent the order on which a /// style rule appears in a stylesheet, needed to sort them by source order. - rules_source_order: usize, + rules_source_order: u32, /// Selector dependencies used to compute restyle hints. dependencies: DependencySet, @@ -584,7 +577,7 @@ impl Stylist { // FIXME(emilio): When we've taken rid of the cascade we can just // use into_iter. self.rule_tree.insert_ordered_rules_with_important( - declarations.into_iter().map(|a| (a.source.clone(), a.level)), + declarations.into_iter().map(|a| (a.source.clone(), a.level())), guards) } None => self.rule_tree.root(), @@ -770,7 +763,7 @@ impl Stylist { let rule_node = self.rule_tree.insert_ordered_rules_with_important( - declarations.into_iter().map(|a| (a.source, a.level)), + declarations.into_iter().map(|a| a.order_and_level()), guards); if rule_node == self.rule_tree.root() { None @@ -975,7 +968,7 @@ impl Stylist { context: &mut MatchingContext, flags_setter: &mut F) where E: TElement, - V: Push + VecLike + ::std::fmt::Debug, + V: Push + VecLike + Debug, F: FnMut(&E, ElementSelectorFlags), { debug_assert!(!self.is_device_dirty); @@ -1027,7 +1020,7 @@ impl Stylist { if applicable_declarations.len() != length_before_preshints { if cfg!(debug_assertions) { for declaration in &applicable_declarations[length_before_preshints..] { - assert_eq!(declaration.level, CascadeLevel::PresHints); + assert_eq!(declaration.level(), CascadeLevel::PresHints); } } // Note the existence of presentational attributes so that the @@ -1222,7 +1215,7 @@ impl Stylist { CascadeLevel::StyleAttributeNormal) ]; let rule_node = - self.rule_tree.insert_ordered_rules(v.into_iter().map(|a| (a.source, a.level))); + self.rule_tree.insert_ordered_rules(v.into_iter().map(|a| a.order_and_level())); // This currently ignores visited styles. It appears to be used for // font styles in via Servo_StyleSet_ResolveForDeclarations. @@ -1513,11 +1506,13 @@ pub struct Rule { /// The ancestor hashes associated with the selector. #[cfg_attr(feature = "servo", ignore_heap_size_of = "No heap data")] pub hashes: AncestorHashes, + /// The source order this style rule appears in. Note that we only use + /// three bytes to store this value in ApplicableDeclarationsBlock, so + /// we could repurpose that storage here if we needed to. + pub source_order: u32, /// The actual style rule. #[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")] pub style_rule: Arc>, - /// The source order this style rule appears in. - pub source_order: usize, } impl SelectorMapEntry for Rule { @@ -1541,19 +1536,18 @@ impl Rule { pub fn to_applicable_declaration_block(&self, level: CascadeLevel) -> ApplicableDeclarationBlock { - ApplicableDeclarationBlock { - source: StyleSource::Style(self.style_rule.clone()), - source_order: self.source_order, - specificity: self.specificity(), - level: level, - } + let source = StyleSource::Style(self.style_rule.clone()); + ApplicableDeclarationBlock::new(source, + self.source_order, + level, + self.specificity()) } /// Creates a new Rule. pub fn new(selector: Selector, hashes: AncestorHashes, style_rule: Arc>, - source_order: usize) + source_order: u32) -> Self { Rule { @@ -1564,38 +1558,3 @@ impl Rule { } } } - -/// A property declaration together with its precedence among rules of equal -/// specificity so that we can sort them. -/// -/// This represents the declarations in a given declaration block for a given -/// importance. -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Debug, Clone, PartialEq)] -pub struct ApplicableDeclarationBlock { - /// The style source, either a style rule, or a property declaration block. - #[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")] - pub source: StyleSource, - /// The source order of this block. - pub source_order: usize, - /// The specificity of the selector this block is represented by. - pub specificity: u32, - /// The cascade level this applicable declaration block is in. - pub level: CascadeLevel, -} - -impl ApplicableDeclarationBlock { - /// Constructs an applicable declaration block from a given property - /// declaration block and importance. - #[inline] - pub fn from_declarations(declarations: Arc>, - level: CascadeLevel) - -> Self { - ApplicableDeclarationBlock { - source: StyleSource::Declarations(declarations), - source_order: 0, - specificity: 0, - level: level, - } - } -} diff --git a/tests/unit/style/stylist.rs b/tests/unit/style/stylist.rs index 17faaddd04a..ee4ac0b53f7 100644 --- a/tests/unit/style/stylist.rs +++ b/tests/unit/style/stylist.rs @@ -45,7 +45,7 @@ fn get_mock_rules(css_selectors: &[&str]) -> (Vec>, SharedRwLock) { let guard = shared_lock.read(); let rule = locked.read_with(&guard); rule.selectors.0.iter().map(|s| { - Rule::new(s.selector.clone(), s.hashes.clone(), locked.clone(), i) + Rule::new(s.selector.clone(), s.hashes.clone(), locked.clone(), i as u32) }).collect() }).collect(), shared_lock) } diff --git a/tests/unit/stylo/size_of.rs b/tests/unit/stylo/size_of.rs index 9bf499ea844..d863cb816a8 100644 --- a/tests/unit/stylo/size_of.rs +++ b/tests/unit/stylo/size_of.rs @@ -6,11 +6,11 @@ use selectors::gecko_like_types as dummies; use servo_arc::Arc; use std::mem::{size_of, align_of}; use style; +use style::applicable_declarations::ApplicableDeclarationBlock; use style::data::{ComputedStyle, ElementData, ElementStyles}; use style::gecko::selector_parser as real; use style::properties::ComputedValues; -use style::rule_tree::StrongRuleNode; -use style::stylist::ApplicableDeclarationBlock; +use style::rule_tree::{RuleNode, StrongRuleNode}; #[test] fn size_of_selectors_dummy_types() { @@ -27,7 +27,7 @@ fn size_of_selectors_dummy_types() { // The size of this is critical to performance on the bloom-basic microbenchmark. // When iterating over a large Rule array, we want to be able to fast-reject // selectors (with the inline hashes) with as few cache misses as possible. -size_of_test!(test_size_of_rule, style::stylist::Rule, 40); +size_of_test!(test_size_of_rule, style::stylist::Rule, 32); size_of_test!(test_size_of_option_arc_cv, Option>, 8); size_of_test!(test_size_of_option_rule_node, Option, 8); @@ -37,7 +37,11 @@ size_of_test!(test_size_of_element_data, ElementData, 56); size_of_test!(test_size_of_property_declaration, style::properties::PropertyDeclaration, 32); -size_of_test!(test_size_of_application_declaration_block, ApplicableDeclarationBlock, 32); +size_of_test!(test_size_of_application_declaration_block, ApplicableDeclarationBlock, 24); + +// FIXME(bholley): This can shrink with a little bit of work. +// See https://github.com/servo/servo/issues/17280 +size_of_test!(test_size_of_rule_node, RuleNode, 96); // This is huge, but we allocate it on the stack and then never move it, // we only pass `&mut SourcePropertyDeclaration` references around.