aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/style/invalidation/mod.rs293
-rw-r--r--components/style/invalidation/stylesheets.rs296
-rw-r--r--components/style/stylesheet_set.rs2
3 files changed, 299 insertions, 292 deletions
diff --git a/components/style/invalidation/mod.rs b/components/style/invalidation/mod.rs
index 93f0d8783ad..6107894b00d 100644
--- a/components/style/invalidation/mod.rs
+++ b/components/style/invalidation/mod.rs
@@ -2,295 +2,6 @@
* 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.
+//! Different bits of code related to invalidating style.
-#![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;
- }
- }
- }
-}
+pub mod stylesheets;
diff --git a/components/style/invalidation/stylesheets.rs b/components/style/invalidation/stylesheets.rs
new file mode 100644
index 00000000000..93f0d8783ad
--- /dev/null
+++ b/components/style/invalidation/stylesheets.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/stylesheet_set.rs b/components/style/stylesheet_set.rs
index 2908d81bef4..5746446d2cf 100644
--- a/components/style/stylesheet_set.rs
+++ b/components/style/stylesheet_set.rs
@@ -5,7 +5,7 @@
//! A centralized set of stylesheets for a document.
use dom::TElement;
-use invalidation::StylesheetInvalidationSet;
+use invalidation::stylesheets::StylesheetInvalidationSet;
use shared_lock::SharedRwLockReadGuard;
use std::slice;
use stylearc::Arc;