aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/style/data.rs5
-rw-r--r--components/style/gecko/data.rs15
-rw-r--r--components/style/gecko/generated/bindings.rs4
-rw-r--r--components/style/invalidation/mod.rs296
-rw-r--r--components/style/lib.rs1
-rw-r--r--components/style/restyle_hints.rs2
-rw-r--r--components/style/stylesheet_set.rs70
-rw-r--r--components/style/stylesheets.rs392
-rw-r--r--components/style/stylist.rs243
-rw-r--r--ports/geckolib/glue.rs44
-rw-r--r--tests/unit/style/media_queries.rs31
11 files changed, 853 insertions, 250 deletions
diff --git a/components/style/data.rs b/components/style/data.rs
index 90ca649e461..b16f918f949 100644
--- a/components/style/data.rs
+++ b/components/style/data.rs
@@ -388,6 +388,11 @@ impl StoredRestyleHint {
self.0.insert(other.0)
}
+ /// Contains whether the whole subtree is invalid.
+ pub fn contains_subtree(&self) -> bool {
+ self.0.contains(&RestyleHint::subtree())
+ }
+
/// Insert another restyle hint, effectively resulting in the union of both.
pub fn insert_from(&mut self, other: &Self) {
self.0.insert_from(&other.0)
diff --git a/components/style/gecko/data.rs b/components/style/gecko/data.rs
index 88c94a2870f..6a4b1f8c896 100644
--- a/components/style/gecko/data.rs
+++ b/components/style/gecko/data.rs
@@ -6,8 +6,10 @@
use Atom;
use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
+use dom::TElement;
use fnv::FnvHashMap;
use gecko::rules::{CounterStyleRule, FontFaceRule};
+use gecko::wrapper::GeckoElement;
use gecko_bindings::bindings::RawServoStyleSet;
use gecko_bindings::structs::RawGeckoPresContextOwned;
use gecko_bindings::structs::nsIDocument;
@@ -72,13 +74,17 @@ impl PerDocumentStyleDataImpl {
///
/// 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(guard);
+ self.flush_stylesheets::<GeckoElement>(guard, None);
}
/// Recreate the style data if the stylesheets have changed.
- pub fn flush_stylesheets(&mut self, guard: &SharedRwLockReadGuard) {
+ pub fn flush_stylesheets<E>(&mut self,
+ guard: &SharedRwLockReadGuard,
+ document_element: Option<E>)
+ where E: TElement,
+ {
if !self.stylesheets.has_changed() {
return;
}
@@ -90,7 +96,8 @@ impl PerDocumentStyleDataImpl {
let author_style_disabled = self.stylesheets.author_style_disabled();
self.stylist.clear();
- self.stylist.rebuild(self.stylesheets.flush(),
+ let iter = self.stylesheets.flush(document_element);
+ self.stylist.rebuild(iter,
&StylesheetGuards::same(guard),
/* ua_sheets = */ None,
/* stylesheets_changed = */ true,
diff --git a/components/style/gecko/generated/bindings.rs b/components/style/gecko/generated/bindings.rs
index 57cdab8e20c..2793625cd9c 100644
--- a/components/style/gecko/generated/bindings.rs
+++ b/components/style/gecko/generated/bindings.rs
@@ -1804,7 +1804,9 @@ extern "C" {
before_unique_id: u64);
}
extern "C" {
- pub fn Servo_StyleSet_FlushStyleSheets(set: RawServoStyleSetBorrowed);
+ pub fn Servo_StyleSet_FlushStyleSheets(set: RawServoStyleSetBorrowed,
+ doc_elem:
+ RawGeckoElementBorrowedOrNull);
}
extern "C" {
pub fn Servo_StyleSet_NoteStyleSheetsChanged(set:
diff --git a/components/style/invalidation/mod.rs b/components/style/invalidation/mod.rs
new file mode 100644
index 00000000000..93f0d8783ad
--- /dev/null
+++ b/components/style/invalidation/mod.rs
@@ -0,0 +1,296 @@
+/* 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/. */
+
+//! A collection of invalidations due to changes in which stylesheets affect a
+//! document.
+
+#![deny(unsafe_code)]
+
+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, Stylesheet};
+use stylist::Stylist;
+
+/// An invalidation 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.
+ Class(Atom),
+}
+
+impl InvalidationScope {
+ fn is_id(&self) -> bool {
+ matches!(*self, InvalidationScope::ID(..))
+ }
+
+ fn matches<E>(&self, element: E) -> bool
+ where E: TElement,
+ {
+ match *self {
+ InvalidationScope::Class(ref class) => {
+ element.has_class(class)
+ }
+ InvalidationScope::ID(ref id) => {
+ match element.get_id() {
+ Some(element_id) => element_id == *id,
+ None => false,
+ }
+ }
+ }
+ }
+}
+
+/// A set of invalidations due to stylesheet additions.
+///
+/// TODO(emilio): We might be able to do the same analysis for removals and
+/// media query changes too?
+pub struct StylesheetInvalidationSet {
+ /// The style scopes we know we have to restyle so far.
+ invalid_scopes: FnvHashSet<InvalidationScope>,
+ /// Whether the whole document should be invalid.
+ fully_invalid: bool,
+}
+
+impl StylesheetInvalidationSet {
+ /// Create an empty `StylesheetInvalidationSet`.
+ pub fn new() -> Self {
+ Self {
+ invalid_scopes: FnvHashSet::default(),
+ fully_invalid: false,
+ }
+ }
+
+ /// Mark the DOM tree styles' as fully invalid.
+ pub fn invalidate_fully(&mut self) {
+ debug!("StylesheetInvalidationSet::invalidate_fully");
+ self.invalid_scopes.clear();
+ self.fully_invalid = true;
+ }
+
+ /// Analyze the given stylesheet, and collect invalidations from their
+ /// rules, in order to avoid doing a full restyle when we style the document
+ /// next time.
+ pub fn collect_invalidations_for(
+ &mut self,
+ stylist: &Stylist,
+ stylesheet: &Stylesheet,
+ guard: &SharedRwLockReadGuard)
+ {
+ debug!("StylesheetInvalidationSet::collect_invalidations_for");
+ if self.fully_invalid {
+ debug!(" > Fully invalid already");
+ return;
+ }
+
+ if stylesheet.disabled() ||
+ !stylesheet.is_effective_for_device(stylist.device(), guard) {
+ debug!(" > Stylesheet was not effective");
+ return; // Nothing to do here.
+ }
+
+ 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>)
+ where E: TElement,
+ {
+ if let Some(e) = document_element {
+ self.process_invalidations_in_subtree(e);
+ }
+ self.invalid_scopes.clear();
+ self.fully_invalid = false;
+ }
+
+ /// Process style invalidations in a given subtree, that is, look for all
+ /// the relevant scopes in the subtree, and mark as dirty only the relevant
+ /// ones.
+ ///
+ /// Returns whether it invalidated at least one element's style.
+ #[allow(unsafe_code)]
+ fn process_invalidations_in_subtree<E>(&self, element: E) -> bool
+ where E: TElement,
+ {
+ let mut data = match element.mutate_data() {
+ Some(data) => data,
+ None => return false,
+ };
+
+ if !data.has_styles() {
+ return false;
+ }
+
+ if let Some(ref r) = data.get_restyle() {
+ if r.hint.contains_subtree() {
+ debug!("process_invalidations_in_subtree: {:?} was already invalid",
+ element);
+ return false;
+ }
+ }
+
+ if self.fully_invalid {
+ debug!("process_invalidations_in_subtree: fully_invalid({:?})",
+ element);
+ data.ensure_restyle().hint.insert(StoredRestyleHint::subtree());
+ return true;
+ }
+
+ for scope in &self.invalid_scopes {
+ if scope.matches(element) {
+ debug!("process_invalidations_in_subtree: {:?} matched {:?}",
+ element, scope);
+ data.ensure_restyle().hint.insert(StoredRestyleHint::subtree());
+ return true;
+ }
+ }
+
+
+ let mut any_children_invalid = false;
+
+ for child in element.as_node().children() {
+ let child = match child.as_element() {
+ Some(e) => e,
+ None => continue,
+ };
+
+ any_children_invalid |= self.process_invalidations_in_subtree(child);
+ }
+
+ if any_children_invalid {
+ debug!("Children of {:?} changed, setting dirty descendants",
+ element);
+ unsafe { element.set_dirty_descendants() }
+ }
+
+ return any_children_invalid
+ }
+
+ 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()));
+ }
+ }
+ Component::ID(ref id) => {
+ if scope.is_none() {
+ *scope = Some(InvalidationScope::ID(id.clone()));
+ }
+ }
+ _ => {
+ // Ignore everything else, at least for now.
+ }
+ }
+ }
+
+ /// Collect a style scopes for a given selector.
+ ///
+ /// We look at the outermost class or id selector to the left of an ancestor
+ /// combinator, in order to restyle only a given subtree.
+ ///
+ /// We prefer id scopes to class scopes, and outermost scopes to innermost
+ /// scopes (to reduce the amount of traversal we need to do).
+ fn collect_scopes(&mut self, selector: &Selector<SelectorImpl>) {
+ debug!("StylesheetInvalidationSet::collect_scopes({:?})", selector);
+
+ let mut scope: Option<InvalidationScope> = None;
+
+ let mut scan = true;
+ let mut iter = selector.inner.complex.iter();
+
+ loop {
+ for component in &mut iter {
+ if scan {
+ Self::scan_component(component, &mut scope);
+ }
+ }
+ match iter.next_sequence() {
+ None => break,
+ Some(combinator) => {
+ scan = combinator.is_ancestor();
+ }
+ }
+ }
+
+ match scope {
+ Some(s) => {
+ debug!(" > Found scope: {:?}", s);
+ self.invalid_scopes.insert(s);
+ }
+ None => {
+ debug!(" > Scope not found");
+
+ // If we didn't find a scope, any element could match this, so
+ // let's just bail out.
+ self.fully_invalid = true;
+ }
+ }
+ }
+
+ /// Collects invalidations for a given CSS rule.
+ fn collect_invalidations_for_rule(
+ &mut self,
+ rule: &CssRule,
+ guard: &SharedRwLockReadGuard)
+ {
+ use stylesheets::CssRule::*;
+ debug!("StylesheetInvalidationSet::collect_invalidations_for_rule");
+ debug_assert!(!self.fully_invalid, "Not worth to be here!");
+
+ match *rule {
+ 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;
+ }
+ }
+ }
+ 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;
+ }
+ }
+ }
+}
diff --git a/components/style/lib.rs b/components/style/lib.rs
index 247daf6222d..3c4241b0df9 100644
--- a/components/style/lib.rs
+++ b/components/style/lib.rs
@@ -110,6 +110,7 @@ pub mod font_face;
pub mod font_metrics;
#[cfg(feature = "gecko")] #[allow(unsafe_code)] pub mod gecko;
#[cfg(feature = "gecko")] #[allow(unsafe_code)] pub mod gecko_bindings;
+pub mod invalidation;
pub mod keyframes;
#[allow(missing_docs)] // TODO.
pub mod logical_geometry;
diff --git a/components/style/restyle_hints.rs b/components/style/restyle_hints.rs
index 57c9f13c672..9e5dc9ad3e4 100644
--- a/components/style/restyle_hints.rs
+++ b/components/style/restyle_hints.rs
@@ -368,7 +368,7 @@ impl RestyleHint {
/// Returns whether this `RestyleHint` represents at least as much restyle
/// work as the specified one.
#[inline]
- pub fn contains(&mut self, other: &Self) -> bool {
+ pub fn contains(&self, other: &Self) -> bool {
self.match_under_self.contains(other.match_under_self) &&
(self.match_later_siblings & other.match_later_siblings) == other.match_later_siblings &&
self.replacements.contains(other.replacements)
diff --git a/components/style/stylesheet_set.rs b/components/style/stylesheet_set.rs
index c21084892a7..2908d81bef4 100644
--- a/components/style/stylesheet_set.rs
+++ b/components/style/stylesheet_set.rs
@@ -4,9 +4,13 @@
//! A centralized set of stylesheets for a document.
+use dom::TElement;
+use invalidation::StylesheetInvalidationSet;
+use shared_lock::SharedRwLockReadGuard;
use std::slice;
use stylearc::Arc;
use stylesheets::Stylesheet;
+use stylist::Stylist;
/// Entry for a StylesheetSet. We don't bother creating a constructor, because
/// there's no sensible defaults for the member variables.
@@ -40,6 +44,9 @@ pub struct StylesheetSet {
/// Has author style been disabled?
author_style_disabled: bool,
+
+ /// The style invalidations that we still haven't processed.
+ invalidations: StylesheetInvalidationSet,
}
impl StylesheetSet {
@@ -49,6 +56,7 @@ impl StylesheetSet {
entries: vec![],
dirty: false,
author_style_disabled: false,
+ invalidations: StylesheetInvalidationSet::new(),
}
}
@@ -63,32 +71,57 @@ impl StylesheetSet {
}
/// Appends a new stylesheet to the current set.
- pub fn append_stylesheet(&mut self, sheet: &Arc<Stylesheet>,
- unique_id: u64) {
+ pub fn append_stylesheet(
+ &mut self,
+ stylist: &Stylist,
+ sheet: &Arc<Stylesheet>,
+ unique_id: u64,
+ guard: &SharedRwLockReadGuard)
+ {
+ debug!("StylesheetSet::append_stylesheet");
self.remove_stylesheet_if_present(unique_id);
self.entries.push(StylesheetSetEntry {
unique_id: unique_id,
sheet: sheet.clone(),
});
self.dirty = true;
+ self.invalidations.collect_invalidations_for(
+ stylist,
+ sheet,
+ guard)
}
/// Prepend a new stylesheet to the current set.
- pub fn prepend_stylesheet(&mut self, sheet: &Arc<Stylesheet>,
- unique_id: u64) {
+ pub fn prepend_stylesheet(
+ &mut self,
+ stylist: &Stylist,
+ sheet: &Arc<Stylesheet>,
+ unique_id: u64,
+ guard: &SharedRwLockReadGuard)
+ {
+ debug!("StylesheetSet::prepend_stylesheet");
self.remove_stylesheet_if_present(unique_id);
self.entries.insert(0, StylesheetSetEntry {
unique_id: unique_id,
sheet: sheet.clone(),
});
self.dirty = true;
+ self.invalidations.collect_invalidations_for(
+ stylist,
+ sheet,
+ guard)
}
/// Insert a given stylesheet before another stylesheet in the document.
- pub fn insert_stylesheet_before(&mut self,
- sheet: &Arc<Stylesheet>,
- unique_id: u64,
- before_unique_id: u64) {
+ pub fn insert_stylesheet_before(
+ &mut self,
+ stylist: &Stylist,
+ sheet: &Arc<Stylesheet>,
+ unique_id: u64,
+ before_unique_id: u64,
+ guard: &SharedRwLockReadGuard)
+ {
+ debug!("StylesheetSet::insert_stylesheet_before");
self.remove_stylesheet_if_present(unique_id);
let index = self.entries.iter().position(|x| {
x.unique_id == before_unique_id
@@ -98,21 +131,30 @@ impl StylesheetSet {
sheet: sheet.clone(),
});
self.dirty = true;
+ self.invalidations.collect_invalidations_for(
+ stylist,
+ sheet,
+ guard)
}
/// Remove a given stylesheet from the set.
pub fn remove_stylesheet(&mut self, unique_id: u64) {
+ debug!("StylesheetSet::remove_stylesheet");
self.remove_stylesheet_if_present(unique_id);
self.dirty = true;
+ // FIXME(emilio): We can do better!
+ self.invalidations.invalidate_fully();
}
/// Notes that the author style has been disabled for this document.
pub fn set_author_style_disabled(&mut self, disabled: bool) {
+ debug!("StylesheetSet::set_author_style_disabled");
if self.author_style_disabled == disabled {
return;
}
self.author_style_disabled = disabled;
self.dirty = true;
+ self.invalidations.invalidate_fully();
}
/// Returns whether the given set has changed from the last flush.
@@ -122,8 +164,17 @@ impl StylesheetSet {
/// Flush the current set, unmarking it as dirty, and returns an iterator
/// over the new stylesheet list.
- pub fn flush(&mut self) -> StylesheetIterator {
+ pub fn flush<E>(&mut self,
+ document_element: Option<E>)
+ -> StylesheetIterator
+ where E: TElement,
+ {
+ debug!("StylesheetSet::flush");
+ debug_assert!(self.dirty);
+
self.dirty = false;
+ self.invalidations.flush(document_element);
+
StylesheetIterator(self.entries.iter())
}
@@ -133,5 +184,6 @@ impl StylesheetSet {
/// FIXME(emilio): Make this more granular.
pub fn force_dirty(&mut self) {
self.dirty = true;
+ self.invalidations.invalidate_fully();
}
}
diff --git a/components/style/stylesheets.rs b/components/style/stylesheets.rs
index bddf1aab2a3..e3dcfaa6b4e 100644
--- a/components/style/stylesheets.rs
+++ b/components/style/stylesheets.rs
@@ -37,11 +37,13 @@ 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::mem::align_of;
use std::os::raw::c_void;
+use std::slice;
use std::sync::atomic::{AtomicBool, Ordering};
use str::starts_with_ignore_ascii_case;
use style_traits::ToCss;
@@ -432,16 +434,6 @@ pub enum CssRuleType {
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,
@@ -475,60 +467,6 @@ impl CssRule {
}
}
- /// 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)]
@@ -1003,6 +941,263 @@ impl DocumentRule {
}
}
+/// 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 {
+ fn process_import(
+ guard: &SharedRwLockReadGuard,
+ device: &Device,
+ quirks_mode: QuirksMode,
+ rule: &ImportRule)
+ -> bool
+ {
+ rule.stylesheet.media.read_with(guard).evaluate(device, quirks_mode)
+ }
+
+ fn process_media(
+ guard: &SharedRwLockReadGuard,
+ device: &Device,
+ quirks_mode: QuirksMode,
+ rule: &MediaRule)
+ -> bool
+ {
+ rule.media_queries.read_with(guard).evaluate(device, quirks_mode)
+ }
+
+ fn process_document(
+ _: &SharedRwLockReadGuard,
+ device: &Device,
+ _: QuirksMode,
+ rule: &DocumentRule)
+ -> bool
+ {
+ rule.condition.evaluate(device)
+ }
+
+ fn process_supports(
+ _: &SharedRwLockReadGuard,
+ _: &Device,
+ _: QuirksMode,
+ rule: &SupportsRule)
+ -> bool
+ {
+ rule.enabled
+ }
+}
+
+/// A filter that processes all the rules in a rule list.
+pub struct AllRules;
+
+impl NestedRuleIterationCondition for AllRules {
+ fn process_import(
+ _: &SharedRwLockReadGuard,
+ _: &Device,
+ _: QuirksMode,
+ _: &ImportRule)
+ -> bool
+ {
+ true
+ }
+
+ /// Whether we should process the nested rules in a given `@media` rule.
+ fn process_media(
+ _: &SharedRwLockReadGuard,
+ _: &Device,
+ _: QuirksMode,
+ _: &MediaRule)
+ -> bool
+ {
+ true
+ }
+
+ /// Whether we should process the nested rules in a given `@-moz-document` rule.
+ fn process_document(
+ _: &SharedRwLockReadGuard,
+ _: &Device,
+ _: QuirksMode,
+ _: &DocumentRule)
+ -> bool
+ {
+ true
+ }
+
+ /// Whether we should process the nested rules in a given `@supports` rule.
+ fn process_supports(
+ _: &SharedRwLockReadGuard,
+ _: &Device,
+ _: QuirksMode,
+ _: &SupportsRule)
+ -> bool
+ {
+ true
+ }
+}
+
+/// 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,
+ }
+ }
+
+ /// Skips all the remaining children of the last nested rule processed.
+ pub fn skip_children(&mut self) {
+ self.stack.pop();
+ }
+}
+
+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> {
+ let mut nested_iter_finished = false;
+ while !self.stack.is_empty() {
+ if nested_iter_finished {
+ self.stack.pop();
+ nested_iter_finished = false;
+ continue;
+ }
+
+ let rule;
+ let sub_iter;
+ {
+ let mut nested_iter = self.stack.last_mut().unwrap();
+ rule = match nested_iter.next() {
+ Some(r) => r,
+ None => {
+ nested_iter_finished = true;
+ continue
+ }
+ };
+
+ 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,
+ };
+ }
+
+ 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,
@@ -1132,14 +1327,30 @@ impl Stylesheet {
/// 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>
+ {
+ self.iter_rules::<'a, 'b, EffectiveRules>(device, guard)
+ }
+
+ /// Return an iterator using the condition `C`.
+ #[inline]
+ pub fn iter_rules<'a, 'b, C>(
+ &'a self,
+ device: &'a Device,
+ guard: &'a SharedRwLockReadGuard<'b>)
+ -> RulesIterator<'a, 'b, C>
+ where C: NestedRuleIterationCondition,
+ {
+ RulesIterator::new(
+ device,
+ self.quirks_mode,
+ guard,
+ &self.rules.read_with(guard))
}
/// Returns whether the stylesheet has been explicitly disabled through the
@@ -1189,51 +1400,20 @@ impl Clone for Stylesheet {
}
}
-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)
}
- })
+ }
}
)+
}
diff --git a/components/style/stylist.rs b/components/style/stylist.rs
index d40a1b8de75..ed29425f523 100644
--- a/components/style/stylist.rs
+++ b/components/style/stylist.rs
@@ -39,10 +39,9 @@ use style_traits::viewport::ViewportConstraints;
use stylearc::Arc;
#[cfg(feature = "gecko")]
use stylesheets::{CounterStyleRule, FontFaceRule};
-use stylesheets::{CssRule, Origin};
-use stylesheets::{StyleRule, Stylesheet, UserAgentStylesheets};
-#[cfg(feature = "servo")]
-use stylesheets::NestedRulesResult;
+use stylesheets::{CssRule, DocumentRule, ImportRule, MediaRule, StyleRule, SupportsRule};
+use stylesheets::{Stylesheet, Origin, UserAgentStylesheets};
+use stylesheets::NestedRuleIterationCondition;
use thread_state;
use viewport::{self, MaybeNew, ViewportRule};
@@ -79,10 +78,7 @@ pub struct Stylist {
/// 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>,
@@ -226,6 +222,57 @@ impl From<StyleRuleInclusion> for RuleInclusion {
}
}
+/// A filter that filters over effective rules, but allowing all potentially
+/// effective `@media` rules.
+pub struct PotentiallyEffectiveMediaRules;
+
+impl NestedRuleIterationCondition for PotentiallyEffectiveMediaRules {
+ fn process_import(
+ _: &SharedRwLockReadGuard,
+ _: &Device,
+ _: QuirksMode,
+ _: &ImportRule)
+ -> bool
+ {
+ true
+ }
+
+ fn process_media(
+ _: &SharedRwLockReadGuard,
+ _: &Device,
+ _: QuirksMode,
+ _: &MediaRule)
+ -> bool
+ {
+ true
+ }
+
+ /// 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
+ {
+ use stylesheets::EffectiveRules;
+ EffectiveRules::process_document(guard, device, quirks_mode, rule)
+ }
+
+ /// 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
+ {
+ use stylesheets::EffectiveRules;
+ EffectiveRules::process_supports(guard, device, quirks_mode, rule)
+ }
+}
+
+
impl Stylist {
/// Construct a new `Stylist`, using given `Device` and `QuirksMode`.
/// If more members are added here, think about whether they should
@@ -234,7 +281,7 @@ impl Stylist {
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,
@@ -364,8 +411,7 @@ impl Stylist {
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| {
@@ -431,31 +477,47 @@ impl Stylist {
fn add_stylesheet<'a>(&mut self,
stylesheet: &Stylesheet,
guard: &SharedRwLockReadGuard,
- extra_data: &mut ExtraStyleData<'a>) {
+ _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);
@@ -474,42 +536,15 @@ impl Stylist {
}
#[cfg(feature = "gecko")]
CssRule::FontFace(ref rule) => {
- extra_data.add_font_face(&rule, stylesheet.origin);
+ _extra_data.add_font_face(&rule, stylesheet.origin);
}
#[cfg(feature = "gecko")]
CssRule::CounterStyle(ref rule) => {
- extra_data.add_counter_style(guard, &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());
}
}
@@ -518,12 +553,7 @@ impl 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)
@@ -536,11 +566,6 @@ impl Stylist {
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.
///
@@ -791,44 +816,62 @@ impl Stylist {
device.account_for_viewport_rule(constraints);
}
- fn mq_eval_changed(guard: &SharedRwLockReadGuard, rules: &[CssRule],
- before: &Device, after: &Device, quirks_mode: QuirksMode) -> bool {
- for rule in rules {
- let changed = rule.with_nested_rules_mq_and_doc_rule(guard,
- |result| {
- let rules = match result {
- NestedRulesResult::Rules(rules) => rules,
- NestedRulesResult::RulesWithMediaQueries(rules, mq) => {
- if mq.evaluate(before, quirks_mode) != mq.evaluate(after, quirks_mode) {
- return true;
- }
- rules
- },
- NestedRulesResult::RulesWithDocument(rules, doc_rule) => {
- if !doc_rule.condition.evaluate(before) {
- return false;
- }
- rules
- },
- };
- mq_eval_changed(guard, rules, before, after, quirks_mode)
- });
- if changed {
- return true
- }
- }
- false
- }
self.is_device_dirty |= stylesheets.iter().any(|stylesheet| {
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)
+ let mut iter =
+ stylesheet.iter_rules::<PotentiallyEffectiveMediaRules>(
+ &self.device,
+ guard);
+
+ while let Some(rule) = iter.next() {
+ match *rule {
+ CssRule::Style(..) |
+ CssRule::Namespace(..) |
+ CssRule::FontFace(..) |
+ CssRule::CounterStyle(..) |
+ CssRule::Supports(..) |
+ CssRule::Keyframes(..) |
+ CssRule::Page(..) |
+ CssRule::Viewport(..) |
+ CssRule::Document(..) => {
+ // Not affected by device changes.
+ continue;
+ }
+ CssRule::Import(ref lock) => {
+ let import_rule = lock.read_with(guard);
+ let mq = import_rule.stylesheet.media.read_with(guard);
+ let effective_now = mq.evaluate(&self.device, self.quirks_mode);
+ if effective_now != mq.evaluate(&device, self.quirks_mode) {
+ return true;
+ }
+
+ if !effective_now {
+ iter.skip_children();
+ }
+ }
+ CssRule::Media(ref lock) => {
+ let media_rule = lock.read_with(guard);
+ let mq = media_rule.media_queries.read_with(guard);
+ let effective_now = mq.evaluate(&self.device, self.quirks_mode);
+ if effective_now != mq.evaluate(&device, self.quirks_mode) {
+ return true;
+ }
+
+ if !effective_now {
+ iter.skip_children();
+ }
+ }
+ }
+ }
+
+ return false;
});
- self.device = Arc::new(device);
+ self.device = device;
}
/// Returns the viewport constraints that apply to this document because of
@@ -1119,7 +1162,7 @@ impl Stylist {
}
/// 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
}
@@ -1146,7 +1189,11 @@ impl Drop for Stylist {
/// 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;
@@ -1160,17 +1207,17 @@ impl<'a> SelectorVisitor for AttributeAndStateDependencyVisitor<'a> {
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
}
diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs
index 180da210779..37c9c70f6aa 100644
--- a/ports/geckolib/glue.rs
+++ b/ports/geckolib/glue.rs
@@ -26,6 +26,7 @@ use style::gecko::selector_parser::PseudoElement;
use style::gecko::traversal::RecalcStyleOnly;
use style::gecko::wrapper::GeckoElement;
use style::gecko_bindings::bindings;
+use style::gecko_bindings::bindings::{RawGeckoElementBorrowed, RawGeckoElementBorrowedOrNull};
use style::gecko_bindings::bindings::{RawGeckoKeyframeListBorrowed, RawGeckoKeyframeListBorrowedMut};
use style::gecko_bindings::bindings::{RawServoDeclarationBlockBorrowed, RawServoDeclarationBlockStrong};
use style::gecko_bindings::bindings::{RawServoDocumentRule, RawServoDocumentRuleBorrowed};
@@ -50,7 +51,6 @@ use style::gecko_bindings::bindings::RawGeckoAnimationPropertySegmentBorrowed;
use style::gecko_bindings::bindings::RawGeckoCSSPropertyIDListBorrowed;
use style::gecko_bindings::bindings::RawGeckoComputedKeyframeValuesListBorrowedMut;
use style::gecko_bindings::bindings::RawGeckoComputedTimingBorrowed;
-use style::gecko_bindings::bindings::RawGeckoElementBorrowed;
use style::gecko_bindings::bindings::RawGeckoFontFaceRuleListBorrowedMut;
use style::gecko_bindings::bindings::RawGeckoServoStyleRuleListBorrowedMut;
use style::gecko_bindings::bindings::RawServoAnimationValueBorrowed;
@@ -732,9 +732,16 @@ pub extern "C" fn Servo_StyleSheet_ClearAndUpdate(stylesheet: RawServoStyleSheet
pub extern "C" fn Servo_StyleSet_AppendStyleSheet(raw_data: RawServoStyleSetBorrowed,
raw_sheet: RawServoStyleSheetBorrowed,
unique_id: u64) {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
+ let mut data = &mut *data;
let sheet = HasArcFFI::as_arc(&raw_sheet);
- data.stylesheets.append_stylesheet(sheet, unique_id);
+ let guard = global_style_data.shared_lock.read();
+ data.stylesheets.append_stylesheet(
+ &data.stylist,
+ sheet,
+ unique_id,
+ &guard);
data.clear_stylist();
}
@@ -742,9 +749,16 @@ pub extern "C" fn Servo_StyleSet_AppendStyleSheet(raw_data: RawServoStyleSetBorr
pub extern "C" fn Servo_StyleSet_PrependStyleSheet(raw_data: RawServoStyleSetBorrowed,
raw_sheet: RawServoStyleSheetBorrowed,
unique_id: u64) {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
+ let mut data = &mut *data;
let sheet = HasArcFFI::as_arc(&raw_sheet);
- data.stylesheets.prepend_stylesheet(sheet, unique_id);
+ let guard = global_style_data.shared_lock.read();
+ data.stylesheets.prepend_stylesheet(
+ &data.stylist,
+ sheet,
+ unique_id,
+ &guard);
data.clear_stylist();
}
@@ -753,9 +767,17 @@ pub extern "C" fn Servo_StyleSet_InsertStyleSheetBefore(raw_data: RawServoStyleS
raw_sheet: RawServoStyleSheetBorrowed,
unique_id: u64,
before_unique_id: u64) {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
+ let mut data = &mut *data;
let sheet = HasArcFFI::as_arc(&raw_sheet);
- data.stylesheets.insert_stylesheet_before(sheet, unique_id, before_unique_id);
+ let guard = global_style_data.shared_lock.read();
+ data.stylesheets.insert_stylesheet_before(
+ &data.stylist,
+ sheet,
+ unique_id,
+ before_unique_id,
+ &guard);
data.clear_stylist();
}
@@ -768,16 +790,22 @@ pub extern "C" fn Servo_StyleSet_RemoveStyleSheet(raw_data: RawServoStyleSetBorr
}
#[no_mangle]
-pub extern "C" fn Servo_StyleSet_FlushStyleSheets(raw_data: RawServoStyleSetBorrowed) {
+pub extern "C" fn Servo_StyleSet_FlushStyleSheets(
+ raw_data: RawServoStyleSetBorrowed,
+ doc_element: RawGeckoElementBorrowedOrNull)
+{
let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read();
let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
- data.flush_stylesheets(&guard);
+ let doc_element = doc_element.map(GeckoElement);
+ data.flush_stylesheets(&guard, doc_element);
}
#[no_mangle]
-pub extern "C" fn Servo_StyleSet_NoteStyleSheetsChanged(raw_data: RawServoStyleSetBorrowed,
- author_style_disabled: bool) {
+pub extern "C" fn Servo_StyleSet_NoteStyleSheetsChanged(
+ raw_data: RawServoStyleSetBorrowed,
+ author_style_disabled: bool)
+{
let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
data.stylesheets.force_dirty();
data.stylesheets.set_author_style_disabled(author_style_disabled);
diff --git a/tests/unit/style/media_queries.rs b/tests/unit/style/media_queries.rs
index f7ce523aa16..30530939bb6 100644
--- a/tests/unit/style/media_queries.rs
+++ b/tests/unit/style/media_queries.rs
@@ -13,7 +13,7 @@ use style::media_queries::*;
use style::servo::media_queries::*;
use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard};
use style::stylearc::Arc;
-use style::stylesheets::{Stylesheet, Origin, CssRule, NestedRulesResult};
+use style::stylesheets::{AllRules, Stylesheet, Origin, CssRule};
use style::values::specified;
use style_traits::ToCss;
@@ -39,31 +39,16 @@ fn test_media_rule<F>(css: &str, callback: F)
let stylesheet = Stylesheet::from_str(
css, url, Origin::Author, media_list, lock,
None, &CSSErrorReporterTest, QuirksMode::NoQuirks, 0u64);
+ let dummy = Device::new(MediaType::Screen, TypedSize2D::new(200.0, 100.0));
let mut rule_count = 0;
let guard = stylesheet.shared_lock.read();
- media_queries(&guard, &stylesheet.rules.read_with(&guard).0, &mut |mq| {
- rule_count += 1;
- callback(mq, css);
- });
- assert!(rule_count > 0, css_str);
-}
-
-fn media_queries<F>(guard: &SharedRwLockReadGuard, rules: &[CssRule], f: &mut F)
- where F: FnMut(&MediaList),
-{
- for rule in rules {
- rule.with_nested_rules_mq_and_doc_rule(guard, |result| {
- match result {
- NestedRulesResult::Rules(rules) |
- NestedRulesResult::RulesWithDocument(rules, _) => {
- media_queries(guard, rules, f)
- },
- NestedRulesResult::RulesWithMediaQueries(_, mq) => {
- f(mq)
- }
- }
- })
+ for rule in stylesheet.iter_rules::<AllRules>(&dummy, &guard) {
+ if let CssRule::Media(ref lock) = *rule {
+ rule_count += 1;
+ callback(&lock.read_with(&guard).media_queries.read_with(&guard), css);
+ }
}
+ assert!(rule_count > 0, css_str);
}
fn media_query_test(device: &Device, css: &str, expected_rule_count: usize) {