--- a/servo/components/style/gecko/data.rs
+++ b/servo/components/style/gecko/data.rs
@@ -69,17 +69,17 @@ impl PerDocumentStyleData {
}
}
impl PerDocumentStyleDataImpl {
/// Reset the device state because it may have changed.
///
/// Implies also a stylesheet flush.
pub fn reset_device(&mut self, guard: &SharedRwLockReadGuard) {
- Arc::get_mut(self.stylist.device_mut()).unwrap().reset();
+ self.stylist.device_mut().reset();
self.stylesheets.force_dirty();
self.flush_stylesheets::<GeckoElement>(guard, None);
}
/// Recreate the style data if the stylesheets have changed.
pub fn flush_stylesheets<E>(&mut self,
guard: &SharedRwLockReadGuard,
document_element: Option<E>)
--- a/servo/components/style/gecko_string_cache/mod.rs
+++ b/servo/components/style/gecko_string_cache/mod.rs
@@ -29,19 +29,19 @@ pub mod atom_macro {
include!(concat!(env!("OUT_DIR"), "/gecko/atom_macro.rs"));
}
#[macro_use]
pub mod namespace;
pub use self::namespace::{Namespace, WeakNamespace};
-// macro_rules! local_name {
-// ($s: tt) => { atom!($s) }
-// }
+macro_rules! local_name {
+ ($s: tt) => { atom!($s) }
+}
/// A strong reference to a Gecko atom.
#[derive(PartialEq, Eq)]
pub struct Atom(*mut WeakAtom);
/// An atom *without* a strong reference.
///
/// Only usable as `&'a WeakAtom`,
--- a/servo/components/style/invalidation/mod.rs
+++ b/servo/components/style/invalidation/mod.rs
@@ -8,17 +8,17 @@
use Atom;
use data::StoredRestyleHint;
use dom::{TElement, TNode};
use fnv::FnvHashSet;
use selector_parser::SelectorImpl;
use selectors::parser::{Component, Selector};
use shared_lock::SharedRwLockReadGuard;
-use stylesheets::{CssRule, CssRules, Stylesheet};
+use stylesheets::{CssRule, Stylesheet};
use stylist::Stylist;
/// A style scope represents a kind of subtree that may need to be restyled.
#[derive(Debug, Hash, Eq, PartialEq)]
enum InvalidationScope {
/// All the descendants of an element with a given id.
ID(Atom),
/// All the descendants of an element with a given class name.
@@ -90,20 +90,23 @@ impl StylesheetInvalidationSet {
}
if stylesheet.disabled() ||
!stylesheet.is_effective_for_device(stylist.device(), guard) {
debug!(" > Stylesheet was not effective");
return; // Nothing to do here.
}
- self.collect_invalidations_for_rule_list(
- stylesheet.rules.read_with(guard),
- stylist,
- guard);
+ for rule in stylesheet.effective_rules(stylist.device(), guard) {
+ self.collect_invalidations_for_rule(rule, guard);
+ if self.fully_invalid {
+ self.invalid_scopes.clear();
+ break;
+ }
+ }
debug!(" > resulting invalidations: {:?}", self.invalid_scopes);
debug!(" > fully_invalid: {}", self.fully_invalid);
}
/// Clears the invalidation set, invalidating elements as needed if
/// `document_element` is provided.
pub fn flush<E>(&mut self, document_element: Option<E>)
@@ -174,33 +177,16 @@ impl StylesheetInvalidationSet {
debug!("Children of {:?} changed, setting dirty descendants",
element);
unsafe { element.set_dirty_descendants() }
}
return any_children_invalid
}
- /// Collects invalidations for a given list of CSS rules.
- ///
- /// TODO(emilio): Convert stylesheet.effective_rules into an iterator to
- /// share code. This needs the ability to stop ASAP.
- fn collect_invalidations_for_rule_list(
- &mut self,
- rules: &CssRules,
- stylist: &Stylist,
- guard: &SharedRwLockReadGuard)
- {
- for rule in &rules.0 {
- if !self.collect_invalidations_for_rule(rule, stylist, guard) {
- return;
- }
- }
- }
-
fn scan_component(
component: &Component<SelectorImpl>,
scope: &mut Option<InvalidationScope>)
{
match *component {
Component::Class(ref class) => {
if scope.as_ref().map_or(true, |s| !s.is_id()) {
*scope = Some(InvalidationScope::Class(class.clone()));
@@ -256,97 +242,50 @@ impl StylesheetInvalidationSet {
// If we didn't found a scope, any selector could match this, so
// let's just bail out.
self.fully_invalid = true;
}
}
}
/// Collects invalidations for a given CSS rule.
- ///
- /// Returns true if it needs to keep collecting invalidations for subsequent
- /// rules in this list.
fn collect_invalidations_for_rule(
&mut self,
rule: &CssRule,
- stylist: &Stylist,
guard: &SharedRwLockReadGuard)
- -> bool
{
use stylesheets::CssRule::*;
debug!("StylesheetInvalidationSet::collect_invalidations_for_rule");
debug_assert!(!self.fully_invalid, "Not worth to be here!");
match *rule {
- Document(ref lock) => {
- let doc_rule = lock.read_with(guard);
- if !doc_rule.condition.evaluate(stylist.device()) {
- return false; // Won't apply anything else after this.
- }
- }
Style(ref lock) => {
let style_rule = lock.read_with(guard);
for selector in &style_rule.selectors.0 {
self.collect_scopes(selector);
if self.fully_invalid {
- return false;
+ return;
}
}
}
- Namespace(..) => {
- // Irrelevant to which selector scopes match.
- }
- // NB: We need to do it here, we won't visit the appropriate sheet
- // otherwise!
- Import(ref lock) => {
- let import_rule = lock.read_with(guard);
-
- let mq = import_rule.stylesheet.media.read_with(guard);
- if !mq.evaluate(stylist.device(), stylist.quirks_mode()) {
- return true;
- }
-
- self.collect_invalidations_for_rule_list(
- import_rule.stylesheet.rules.read_with(guard),
- stylist,
- guard);
- }
- Media(ref lock) => {
- let media_rule = lock.read_with(guard);
-
- let mq = media_rule.media_queries.read_with(guard);
- if !mq.evaluate(stylist.device(), stylist.quirks_mode()) {
- return true;
- }
-
- self.collect_invalidations_for_rule_list(
- media_rule.rules.read_with(guard),
- stylist,
- guard);
- }
- Supports(ref lock) => {
- let supports_rule = lock.read_with(guard);
- if !supports_rule.enabled {
- return true;
- }
-
- self.collect_invalidations_for_rule_list(
- supports_rule.rules.read_with(guard),
- stylist,
- guard);
+ Document(..) |
+ Namespace(..) |
+ Import(..) |
+ Media(..) |
+ Supports(..) => {
+ // Do nothing, relevant nested rules are visited as part of the
+ // iteration.
}
FontFace(..) |
CounterStyle(..) |
Keyframes(..) |
Page(..) |
Viewport(..) => {
debug!(" > Found unsupported rule, marking the whole subtree invalid.");
// TODO(emilio): Can we do better here?
//
// At least in `@page`, we could check the relevant media, I
// guess.
self.fully_invalid = true;
}
}
-
- return !self.fully_invalid
}
}
--- a/servo/components/style/stylesheets.rs
+++ b/servo/components/style/stylesheets.rs
@@ -32,19 +32,21 @@ use parser::{PARSING_MODE_DEFAULT, Parse
use properties::{PropertyDeclarationBlock, parse_property_declaration_list};
use selector_parser::{SelectorImpl, SelectorParser};
use selectors::parser::SelectorList;
#[cfg(feature = "servo")]
use servo_config::prefs::PREFS;
#[cfg(not(feature = "gecko"))]
use servo_url::ServoUrl;
use shared_lock::{SharedRwLock, Locked, ToCssWithGuard, SharedRwLockReadGuard};
+use smallvec::SmallVec;
use std::borrow::Borrow;
use std::cell::Cell;
use std::fmt;
+use std::slice;
use std::sync::atomic::{AtomicBool, Ordering};
use str::starts_with_ignore_ascii_case;
use style_traits::ToCss;
use stylearc::Arc;
use stylist::FnvHashMap;
use supports::SupportsCondition;
use values::{CustomIdent, KeyframesName};
use values::specified::url::SpecifiedUrl;
@@ -338,26 +340,16 @@ pub enum CssRuleType {
// https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#extentions-to-cssrule-interface
Document = 13,
// https://drafts.csswg.org/css-fonts-3/#om-fontfeaturevalues
FontFeatureValues = 14,
// https://drafts.csswg.org/css-device-adapt/#css-rule-interface
Viewport = 15,
}
-/// Result type for with_nested_rules_mq_and_doc_rule()
-pub enum NestedRulesResult<'a> {
- /// Only rules
- Rules(&'a [CssRule]),
- /// Rules with media queries
- RulesWithMediaQueries(&'a [CssRule], &'a MediaList),
- /// Rules with document rule
- RulesWithDocument(&'a [CssRule], &'a DocumentRule)
-}
-
#[allow(missing_docs)]
pub enum SingleRuleParseError {
Syntax,
Hierarchy,
}
impl CssRule {
#[allow(missing_docs)]
@@ -381,70 +373,16 @@ impl CssRule {
match *self {
// CssRule::Charset(..) => State::Start,
CssRule::Import(..) => State::Imports,
CssRule::Namespace(..) => State::Namespaces,
_ => State::Body,
}
}
- /// Call `f` with the slice of rules directly contained inside this rule.
- ///
- /// Note that only some types of rules can contain rules. An empty slice is
- /// used for others.
- ///
- /// This will not recurse down unsupported @supports rules
- pub fn with_nested_rules_mq_and_doc_rule<F, R>(&self, guard: &SharedRwLockReadGuard, mut f: F) -> R
- where F: FnMut(NestedRulesResult) -> R {
- match *self {
- CssRule::Import(ref lock) => {
- let rule = lock.read_with(guard);
- let media = rule.stylesheet.media.read_with(guard);
- let rules = rule.stylesheet.rules.read_with(guard);
- // FIXME(emilio): Include the nested rules if the stylesheet is
- // loaded.
- f(NestedRulesResult::RulesWithMediaQueries(&rules.0, &media))
- }
- CssRule::Namespace(_) |
- CssRule::Style(_) |
- CssRule::FontFace(_) |
- CssRule::CounterStyle(_) |
- CssRule::Viewport(_) |
- CssRule::Keyframes(_) |
- CssRule::Page(_) => {
- f(NestedRulesResult::Rules(&[]))
- }
- CssRule::Media(ref lock) => {
- let media_rule = lock.read_with(guard);
- let mq = media_rule.media_queries.read_with(guard);
- let rules = &media_rule.rules.read_with(guard).0;
- f(NestedRulesResult::RulesWithMediaQueries(rules, &mq))
- }
- CssRule::Supports(ref lock) => {
- let supports_rule = lock.read_with(guard);
- let enabled = supports_rule.enabled;
- if enabled {
- let rules = &supports_rule.rules.read_with(guard).0;
- f(NestedRulesResult::Rules(rules))
- } else {
- f(NestedRulesResult::Rules(&[]))
- }
- }
- CssRule::Document(ref lock) => {
- if cfg!(feature = "gecko") {
- let document_rule = lock.read_with(guard);
- let rules = &document_rule.rules.read_with(guard).0;
- f(NestedRulesResult::RulesWithDocument(rules, &document_rule))
- } else {
- unimplemented!()
- }
- }
- }
- }
-
// input state is None for a nested rule
// Returns a parsed CSS rule and the final state of the parser
#[allow(missing_docs)]
pub fn parse(css: &str,
parent_stylesheet: &Stylesheet,
state: Option<State>,
loader: Option<&StylesheetLoader>)
-> Result<(Self, State), SingleRuleParseError> {
@@ -901,16 +839,211 @@ impl DocumentRule {
DocumentRule {
condition: self.condition.clone(),
rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock))),
source_location: self.source_location.clone(),
}
}
}
+/// A trait that describes statically which rules are iterated for a given
+/// RulesIterator.
+pub trait NestedRuleIterationCondition {
+ /// Whether we should process the nested rules in a given `@import` rule.
+ fn process_import(
+ guard: &SharedRwLockReadGuard,
+ device: &Device,
+ quirks_mode: QuirksMode,
+ rule: &ImportRule)
+ -> bool;
+
+ /// Whether we should process the nested rules in a given `@media` rule.
+ fn process_media(
+ guard: &SharedRwLockReadGuard,
+ device: &Device,
+ quirks_mode: QuirksMode,
+ rule: &MediaRule)
+ -> bool;
+
+ /// Whether we should process the nested rules in a given `@-moz-document` rule.
+ fn process_document(
+ guard: &SharedRwLockReadGuard,
+ device: &Device,
+ quirks_mode: QuirksMode,
+ rule: &DocumentRule)
+ -> bool;
+
+ /// Whether we should process the nested rules in a given `@supports` rule.
+ fn process_supports(
+ guard: &SharedRwLockReadGuard,
+ device: &Device,
+ quirks_mode: QuirksMode,
+ rule: &SupportsRule)
+ -> bool;
+}
+
+/// A struct that represents the condition that a rule applies to the document.
+pub struct EffectiveRules;
+
+impl NestedRuleIterationCondition for EffectiveRules {
+ /// Whether we should process the nested rules in a given `@import` rule.
+ fn process_import(
+ guard: &SharedRwLockReadGuard,
+ device: &Device,
+ quirks_mode: QuirksMode,
+ rule: &ImportRule)
+ -> bool
+ {
+ rule.stylesheet.media.read_with(guard).evaluate(device, quirks_mode)
+ }
+
+ /// Whether we should process the nested rules in a given `@media` rule.
+ fn process_media(
+ guard: &SharedRwLockReadGuard,
+ device: &Device,
+ quirks_mode: QuirksMode,
+ rule: &MediaRule)
+ -> bool
+ {
+ rule.media_queries.read_with(guard).evaluate(device, quirks_mode)
+ }
+
+ /// Whether we should process the nested rules in a given `@-moz-document` rule.
+ fn process_document(
+ _: &SharedRwLockReadGuard,
+ device: &Device,
+ _: QuirksMode,
+ rule: &DocumentRule)
+ -> bool
+ {
+ rule.condition.evaluate(device)
+ }
+
+ /// Whether we should process the nested rules in a given `@supports` rule.
+ fn process_supports(
+ _: &SharedRwLockReadGuard,
+ _: &Device,
+ _: QuirksMode,
+ rule: &SupportsRule)
+ -> bool
+ {
+ rule.enabled
+ }
+}
+
+/// An iterator over all the effective rules of a stylesheet.
+///
+/// NOTE: This iterator recurses into `@import` rules.
+pub type EffectiveRulesIterator<'a, 'b> = RulesIterator<'a, 'b, EffectiveRules>;
+
+/// An iterator over a list of rules.
+pub struct RulesIterator<'a, 'b, C>
+ where 'b: 'a,
+ C: NestedRuleIterationCondition + 'static,
+{
+ device: &'a Device,
+ quirks_mode: QuirksMode,
+ guard: &'a SharedRwLockReadGuard<'b>,
+ stack: SmallVec<[slice::Iter<'a, CssRule>; 3]>,
+ _phantom: ::std::marker::PhantomData<C>,
+}
+
+impl<'a, 'b, C> RulesIterator<'a, 'b, C>
+ where 'b: 'a,
+ C: NestedRuleIterationCondition + 'static,
+{
+ /// Creates a new `RulesIterator` to iterate over `rules`.
+ pub fn new(
+ device: &'a Device,
+ quirks_mode: QuirksMode,
+ guard: &'a SharedRwLockReadGuard<'b>,
+ rules: &'a CssRules)
+ -> Self
+ {
+ let mut stack = SmallVec::new();
+ stack.push(rules.0.iter());
+ Self {
+ device: device,
+ quirks_mode: quirks_mode,
+ guard: guard,
+ stack: stack,
+ _phantom: ::std::marker::PhantomData,
+ }
+ }
+}
+
+impl<'a, 'b, C> Iterator for RulesIterator<'a, 'b, C>
+ where 'b: 'a,
+ C: NestedRuleIterationCondition + 'static,
+{
+ type Item = &'a CssRule;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ while let Some(mut nested_iter) = self.stack.pop() {
+ let rule = match nested_iter.next() {
+ Some(r) => r,
+ None => continue,
+ };
+
+ let sub_iter = match *rule {
+ CssRule::Import(ref import_rule) => {
+ let import_rule = import_rule.read_with(self.guard);
+
+ if C::process_import(self.guard, self.device, self.quirks_mode, import_rule) {
+ Some(import_rule.stylesheet.rules.read_with(self.guard).0.iter())
+ } else {
+ None
+ }
+ }
+ CssRule::Document(ref doc_rule) => {
+ let doc_rule = doc_rule.read_with(self.guard);
+ if C::process_document(self.guard, self.device, self.quirks_mode, doc_rule) {
+ Some(doc_rule.rules.read_with(self.guard).0.iter())
+ } else {
+ None
+ }
+ }
+ CssRule::Media(ref lock) => {
+ let media_rule = lock.read_with(self.guard);
+ if C::process_media(self.guard, self.device, self.quirks_mode, media_rule) {
+ Some(media_rule.rules.read_with(self.guard).0.iter())
+ } else {
+ None
+ }
+ }
+ CssRule::Supports(ref lock) => {
+ let supports_rule = lock.read_with(self.guard);
+ if C::process_supports(self.guard, self.device, self.quirks_mode, supports_rule) {
+ Some(supports_rule.rules.read_with(self.guard).0.iter())
+ } else {
+ None
+ }
+ }
+ CssRule::Namespace(_) |
+ CssRule::Style(_) |
+ CssRule::FontFace(_) |
+ CssRule::CounterStyle(_) |
+ CssRule::Viewport(_) |
+ CssRule::Keyframes(_) |
+ CssRule::Page(_) => None,
+ };
+
+ self.stack.push(nested_iter);
+
+ if let Some(sub_iter) = sub_iter {
+ self.stack.push(sub_iter);
+ }
+
+ return Some(rule);
+ }
+
+ None
+ }
+}
+
impl Stylesheet {
/// Updates an empty stylesheet from a given string of text.
pub fn update_from_str(existing: &Stylesheet,
css: &str,
url_data: &UrlExtraData,
stylesheet_loader: Option<&StylesheetLoader>,
error_reporter: &ParseErrorReporter,
line_number_offset: u64) {
@@ -1030,24 +1163,28 @@ impl Stylesheet {
///
/// Always true if no associated MediaList exists.
pub fn is_effective_for_device(&self, device: &Device, guard: &SharedRwLockReadGuard) -> bool {
self.media.read_with(guard).evaluate(device, self.quirks_mode)
}
/// Return an iterator over the effective rules within the style-sheet, as
/// according to the supplied `Device`.
- ///
- /// If a condition does not hold, its associated conditional group rule and
- /// nested rules will be skipped. Use `rules` if all rules need to be
- /// examined.
#[inline]
- pub fn effective_rules<F>(&self, device: &Device, guard: &SharedRwLockReadGuard, mut f: F)
- where F: FnMut(&CssRule) {
- effective_rules(&self.rules.read_with(guard).0, device, self.quirks_mode, guard, &mut f);
+ pub fn effective_rules<'a, 'b>(
+ &'a self,
+ device: &'a Device,
+ guard: &'a SharedRwLockReadGuard<'b>)
+ -> EffectiveRulesIterator<'a, 'b>
+ {
+ EffectiveRulesIterator::new(
+ device,
+ self.quirks_mode,
+ guard,
+ &self.rules.read_with(guard))
}
/// Returns whether the stylesheet has been explicitly disabled through the
/// CSSOM.
pub fn disabled(&self) -> bool {
self.disabled.load(Ordering::SeqCst)
}
@@ -1087,61 +1224,30 @@ impl Clone for Stylesheet {
dirty_on_viewport_size_change: AtomicBool::new(
self.dirty_on_viewport_size_change.load(Ordering::SeqCst)),
disabled: AtomicBool::new(self.disabled.load(Ordering::SeqCst)),
quirks_mode: self.quirks_mode,
}
}
}
-fn effective_rules<F>(rules: &[CssRule],
- device: &Device,
- quirks_mode: QuirksMode,
- guard: &SharedRwLockReadGuard,
- f: &mut F)
- where F: FnMut(&CssRule)
-{
- for rule in rules {
- f(rule);
- rule.with_nested_rules_mq_and_doc_rule(guard, |result| {
- let rules = match result {
- NestedRulesResult::Rules(rules) => {
- rules
- },
- NestedRulesResult::RulesWithMediaQueries(rules, media_queries) => {
- if !media_queries.evaluate(device, quirks_mode) {
- return;
- }
- rules
- },
- NestedRulesResult::RulesWithDocument(rules, doc_rule) => {
- if !doc_rule.condition.evaluate(device) {
- return;
- }
- rules
- },
- };
- effective_rules(rules, device, quirks_mode, guard, f)
- })
- }
-}
-
macro_rules! rule_filter {
($( $method: ident($variant:ident => $rule_type: ident), )+) => {
impl Stylesheet {
$(
#[allow(missing_docs)]
pub fn $method<F>(&self, device: &Device, guard: &SharedRwLockReadGuard, mut f: F)
- where F: FnMut(&$rule_type) {
- self.effective_rules(device, guard, |rule| {
+ where F: FnMut(&$rule_type),
+ {
+ for rule in self.effective_rules(device, guard) {
if let CssRule::$variant(ref lock) = *rule {
let rule = lock.read_with(guard);
f(&rule)
}
- })
+ }
}
)+
}
}
}
rule_filter! {
effective_style_rules(Style => StyleRule),
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -74,20 +74,17 @@ pub struct Stylist {
///
/// With Gecko, the device is never changed. Gecko manually tracks whether
/// the device data should be reconstructed, and "resets" the state of the
/// device.
///
/// On Servo, on the other hand, the device is a really cheap representation
/// that is recreated each time some constraint changes and calling
/// `set_device`.
- ///
- /// In both cases, the device is actually _owned_ by the Stylist, and it's
- /// only an `Arc` so we can implement `add_stylesheet` more idiomatically.
- device: Arc<Device>,
+ device: Device,
/// Viewport constraints based on the current device.
viewport_constraints: Option<ViewportConstraints>,
/// If true, the quirks-mode stylesheet is applied.
quirks_mode: QuirksMode,
/// If true, the device has changed, and the stylist needs to be updated.
@@ -229,17 +226,17 @@ impl From<StyleRuleInclusion> for RuleIn
impl Stylist {
/// Construct a new `Stylist`, using given `Device` and `QuirksMode`.
/// If more members are added here, think about whether they should
/// be reset in clear().
#[inline]
pub fn new(device: Device, quirks_mode: QuirksMode) -> Self {
let mut stylist = Stylist {
viewport_constraints: None,
- device: Arc::new(device),
+ device: device,
is_device_dirty: true,
is_cleared: true,
quirks_mode: quirks_mode,
element_map: PerPseudoElementSelectorMap::new(),
pseudos_map: Default::default(),
animations: Default::default(),
precomputed_pseudo_element_decls: Default::default(),
@@ -359,18 +356,17 @@ impl Stylist {
doc_stylesheets.clone(), guards.author, &self.device
).finish(),
};
self.viewport_constraints =
ViewportConstraints::maybe_new(&self.device, &cascaded_rule, self.quirks_mode);
if let Some(ref constraints) = self.viewport_constraints {
- Arc::get_mut(&mut self.device).unwrap()
- .account_for_viewport_rule(constraints);
+ self.device.account_for_viewport_rule(constraints);
}
SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
self.pseudos_map.insert(pseudo, PerPseudoElementSelectorMap::new());
});
extra_data.clear();
@@ -431,36 +427,52 @@ impl Stylist {
fn add_stylesheet<'a>(&mut self,
stylesheet: &Stylesheet,
guard: &SharedRwLockReadGuard,
extra_data: &mut ExtraStyleData<'a>) {
if stylesheet.disabled() || !stylesheet.is_effective_for_device(&self.device, guard) {
return;
}
- // Cheap `Arc` clone so that the closure below can borrow `&mut Stylist`.
- let device = self.device.clone();
-
- stylesheet.effective_rules(&device, guard, |rule| {
+ for rule in stylesheet.effective_rules(&self.device, guard) {
match *rule {
CssRule::Style(ref locked) => {
let style_rule = locked.read_with(&guard);
self.num_declarations += style_rule.block.read_with(&guard).len();
for selector in &style_rule.selectors.0 {
self.num_selectors += 1;
- self.add_rule_to_map(selector, locked, stylesheet);
+
+ let map = if let Some(pseudo) = selector.pseudo_element() {
+ self.pseudos_map
+ .entry(pseudo.canonical())
+ .or_insert_with(PerPseudoElementSelectorMap::new)
+ .borrow_for_origin(&stylesheet.origin)
+ } else {
+ self.element_map.borrow_for_origin(&stylesheet.origin)
+ };
+
+ map.insert(Rule::new(selector.clone(),
+ locked.clone(),
+ self.rules_source_order));
+
self.dependencies.note_selector(selector);
- self.note_for_revalidation(selector);
- self.note_attribute_and_state_dependencies(selector);
+ if needs_revalidation(selector) {
+ self.selectors_for_cache_revalidation.insert(selector.inner.clone());
+ }
+ selector.visit(&mut AttributeAndStateDependencyVisitor {
+ attribute_dependencies: &mut self.attribute_dependencies,
+ style_attribute_dependency: &mut self.style_attribute_dependency,
+ state_dependencies: &mut self.state_dependencies,
+ });
}
self.rules_source_order += 1;
}
- CssRule::Import(ref import) => {
- let import = import.read_with(guard);
- self.add_stylesheet(&import.stylesheet, guard, extra_data)
+ CssRule::Import(..) => {
+ // effective_rules visits the inner stylesheet if
+ // appropriate.
}
CssRule::Keyframes(ref keyframes_rule) => {
let keyframes_rule = keyframes_rule.read_with(guard);
debug!("Found valid keyframes rule: {:?}", *keyframes_rule);
// Don't let a prefixed keyframes animation override a non-prefixed one.
let needs_insertion = keyframes_rule.vendor_prefix.is_none() ||
self.animations.get(keyframes_rule.name.as_atom()).map_or(true, |rule|
@@ -478,74 +490,37 @@ impl Stylist {
}
#[cfg(feature = "gecko")]
CssRule::CounterStyle(ref rule) => {
extra_data.add_counter_style(guard, &rule);
}
// We don't care about any other rule.
_ => {}
}
- });
- }
-
- #[inline]
- fn add_rule_to_map(&mut self,
- selector: &Selector<SelectorImpl>,
- rule: &Arc<Locked<StyleRule>>,
- stylesheet: &Stylesheet)
- {
- let map = if let Some(pseudo) = selector.pseudo_element() {
- self.pseudos_map
- .entry(pseudo.canonical())
- .or_insert_with(PerPseudoElementSelectorMap::new)
- .borrow_for_origin(&stylesheet.origin)
- } else {
- self.element_map.borrow_for_origin(&stylesheet.origin)
- };
-
- map.insert(Rule::new(selector.clone(),
- rule.clone(),
- self.rules_source_order));
- }
-
- #[inline]
- fn note_for_revalidation(&mut self, selector: &Selector<SelectorImpl>) {
- if needs_revalidation(selector) {
- self.selectors_for_cache_revalidation.insert(selector.inner.clone());
}
}
/// Returns whether the given attribute might appear in an attribute
/// selector of some rule in the stylist.
pub fn might_have_attribute_dependency(&self,
local_name: &LocalName)
-> bool {
- #[cfg(feature = "servo")]
- let style_lower_name = local_name!("style");
- #[cfg(feature = "gecko")]
- let style_lower_name = atom!("style");
-
- if *local_name == style_lower_name {
+ if *local_name == local_name!("style") {
self.style_attribute_dependency
} else {
self.attribute_dependencies.might_contain(local_name)
}
}
/// Returns whether the given ElementState bit is relied upon by a selector
/// of some rule in the stylist.
pub fn has_state_dependency(&self, state: ElementState) -> bool {
self.state_dependencies.intersects(state)
}
- #[inline]
- fn note_attribute_and_state_dependencies(&mut self, selector: &Selector<SelectorImpl>) {
- selector.visit(&mut AttributeAndStateDependencyVisitor(self));
- }
-
/// Computes the style for a given "precomputed" pseudo-element, taking the
/// universal rules and applying them.
///
/// If `inherit_all` is true, then all properties are inherited from the
/// parent; otherwise, non-inherited properties are reset to their initial
/// values. The flow constructor uses this flag when constructing anonymous
/// flows.
pub fn precomputed_values_for_pseudo(&self,
@@ -823,17 +798,17 @@ impl Stylist {
let mq = stylesheet.media.read_with(guard);
if mq.evaluate(&self.device, self.quirks_mode) != mq.evaluate(&device, self.quirks_mode) {
return true
}
mq_eval_changed(guard, &stylesheet.rules.read_with(guard).0, &self.device, &device, self.quirks_mode)
});
- self.device = Arc::new(device);
+ self.device = device;
}
/// Returns the viewport constraints that apply to this document because of
/// a @viewport rule.
pub fn viewport_constraints(&self) -> Option<&ViewportConstraints> {
self.viewport_constraints.as_ref()
}
@@ -1114,17 +1089,17 @@ impl Stylist {
}
/// Accessor for a shared reference to the device.
pub fn device(&self) -> &Device {
&self.device
}
/// Accessor for a mutable reference to the device.
- pub fn device_mut(&mut self) -> &mut Arc<Device> {
+ pub fn device_mut(&mut self) -> &mut Device {
&mut self.device
}
/// Accessor for a shared reference to the rule tree.
pub fn rule_tree(&self) -> &RuleTree {
&self.rule_tree
}
}
@@ -1141,41 +1116,45 @@ impl Drop for Stylist {
// TODO(emilio): We can at least assert all the elements in the free
// list are indeed free.
unsafe { self.rule_tree.gc(); }
}
}
/// Visitor to collect names that appear in attribute selectors and any
/// dependencies on ElementState bits.
-struct AttributeAndStateDependencyVisitor<'a>(&'a mut Stylist);
+struct AttributeAndStateDependencyVisitor<'a> {
+ attribute_dependencies: &'a mut BloomFilter,
+ style_attribute_dependency: &'a mut bool,
+ state_dependencies: &'a mut ElementState,
+}
impl<'a> SelectorVisitor for AttributeAndStateDependencyVisitor<'a> {
type Impl = SelectorImpl;
fn visit_attribute_selector(&mut self, _ns: &NamespaceConstraint<&Namespace>,
name: &LocalName, lower_name: &LocalName)
-> bool {
#[cfg(feature = "servo")]
let style_lower_name = local_name!("style");
#[cfg(feature = "gecko")]
let style_lower_name = atom!("style");
if *lower_name == style_lower_name {
- self.0.style_attribute_dependency = true;
+ *self.style_attribute_dependency = true;
} else {
- self.0.attribute_dependencies.insert(&name);
- self.0.attribute_dependencies.insert(&lower_name);
+ self.attribute_dependencies.insert(&name);
+ self.attribute_dependencies.insert(&lower_name);
}
true
}
fn visit_simple_selector(&mut self, s: &Component<SelectorImpl>) -> bool {
if let Component::NonTSPseudoClass(ref p) = *s {
- self.0.state_dependencies.insert(p.state_flag());
+ self.state_dependencies.insert(p.state_flag());
}
true
}
}
/// Visitor determine whether a selector requires cache revalidation.
///
/// Note that we just check simple selectors and eagerly return when the first