зеркало из https://github.com/mozilla/gecko-dev.git
servo: Merge #17078 - stylo: Avoid restyling the whole document when adding stylesheets (from emilio:stylesheet-invalidation-scopes); r=heycam
This is for bug 1357583. Source-Repo: https://github.com/servo/servo Source-Revision: 939716a7bc2ba2fbd8dcfe8b64d2f61edfa99c95 --HG-- extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear extra : subtree_revision : b25a23978bde1dc6abdaaa9621eca4903152efd2
This commit is contained in:
Родитель
035bc8491b
Коммит
6e97f119ed
|
@ -388,6 +388,11 @@ impl StoredRestyleHint {
|
||||||
self.0.insert(other.0)
|
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.
|
/// Insert another restyle hint, effectively resulting in the union of both.
|
||||||
pub fn insert_from(&mut self, other: &Self) {
|
pub fn insert_from(&mut self, other: &Self) {
|
||||||
self.0.insert_from(&other.0)
|
self.0.insert_from(&other.0)
|
||||||
|
|
|
@ -6,8 +6,10 @@
|
||||||
|
|
||||||
use Atom;
|
use Atom;
|
||||||
use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
|
use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
|
||||||
|
use dom::TElement;
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
use gecko::rules::{CounterStyleRule, FontFaceRule};
|
use gecko::rules::{CounterStyleRule, FontFaceRule};
|
||||||
|
use gecko::wrapper::GeckoElement;
|
||||||
use gecko_bindings::bindings::RawServoStyleSet;
|
use gecko_bindings::bindings::RawServoStyleSet;
|
||||||
use gecko_bindings::structs::RawGeckoPresContextOwned;
|
use gecko_bindings::structs::RawGeckoPresContextOwned;
|
||||||
use gecko_bindings::structs::nsIDocument;
|
use gecko_bindings::structs::nsIDocument;
|
||||||
|
@ -72,13 +74,17 @@ impl PerDocumentStyleDataImpl {
|
||||||
///
|
///
|
||||||
/// Implies also a stylesheet flush.
|
/// Implies also a stylesheet flush.
|
||||||
pub fn reset_device(&mut self, guard: &SharedRwLockReadGuard) {
|
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.stylesheets.force_dirty();
|
||||||
self.flush_stylesheets(guard);
|
self.flush_stylesheets::<GeckoElement>(guard, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recreate the style data if the stylesheets have changed.
|
/// 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() {
|
if !self.stylesheets.has_changed() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -90,7 +96,8 @@ impl PerDocumentStyleDataImpl {
|
||||||
|
|
||||||
let author_style_disabled = self.stylesheets.author_style_disabled();
|
let author_style_disabled = self.stylesheets.author_style_disabled();
|
||||||
self.stylist.clear();
|
self.stylist.clear();
|
||||||
self.stylist.rebuild(self.stylesheets.flush(),
|
let iter = self.stylesheets.flush(document_element);
|
||||||
|
self.stylist.rebuild(iter,
|
||||||
&StylesheetGuards::same(guard),
|
&StylesheetGuards::same(guard),
|
||||||
/* ua_sheets = */ None,
|
/* ua_sheets = */ None,
|
||||||
/* stylesheets_changed = */ true,
|
/* stylesheets_changed = */ true,
|
||||||
|
|
|
@ -1804,7 +1804,9 @@ extern "C" {
|
||||||
before_unique_id: u64);
|
before_unique_id: u64);
|
||||||
}
|
}
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn Servo_StyleSet_FlushStyleSheets(set: RawServoStyleSetBorrowed);
|
pub fn Servo_StyleSet_FlushStyleSheets(set: RawServoStyleSetBorrowed,
|
||||||
|
doc_elem:
|
||||||
|
RawGeckoElementBorrowedOrNull);
|
||||||
}
|
}
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn Servo_StyleSet_NoteStyleSheetsChanged(set:
|
pub fn Servo_StyleSet_NoteStyleSheetsChanged(set:
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -110,6 +110,7 @@ pub mod font_face;
|
||||||
pub mod font_metrics;
|
pub mod font_metrics;
|
||||||
#[cfg(feature = "gecko")] #[allow(unsafe_code)] pub mod gecko;
|
#[cfg(feature = "gecko")] #[allow(unsafe_code)] pub mod gecko;
|
||||||
#[cfg(feature = "gecko")] #[allow(unsafe_code)] pub mod gecko_bindings;
|
#[cfg(feature = "gecko")] #[allow(unsafe_code)] pub mod gecko_bindings;
|
||||||
|
pub mod invalidation;
|
||||||
pub mod keyframes;
|
pub mod keyframes;
|
||||||
#[allow(missing_docs)] // TODO.
|
#[allow(missing_docs)] // TODO.
|
||||||
pub mod logical_geometry;
|
pub mod logical_geometry;
|
||||||
|
|
|
@ -368,7 +368,7 @@ impl RestyleHint {
|
||||||
/// Returns whether this `RestyleHint` represents at least as much restyle
|
/// Returns whether this `RestyleHint` represents at least as much restyle
|
||||||
/// work as the specified one.
|
/// work as the specified one.
|
||||||
#[inline]
|
#[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_under_self.contains(other.match_under_self) &&
|
||||||
(self.match_later_siblings & other.match_later_siblings) == other.match_later_siblings &&
|
(self.match_later_siblings & other.match_later_siblings) == other.match_later_siblings &&
|
||||||
self.replacements.contains(other.replacements)
|
self.replacements.contains(other.replacements)
|
||||||
|
|
|
@ -4,9 +4,13 @@
|
||||||
|
|
||||||
//! A centralized set of stylesheets for a document.
|
//! A centralized set of stylesheets for a document.
|
||||||
|
|
||||||
|
use dom::TElement;
|
||||||
|
use invalidation::StylesheetInvalidationSet;
|
||||||
|
use shared_lock::SharedRwLockReadGuard;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use stylearc::Arc;
|
use stylearc::Arc;
|
||||||
use stylesheets::Stylesheet;
|
use stylesheets::Stylesheet;
|
||||||
|
use stylist::Stylist;
|
||||||
|
|
||||||
/// Entry for a StylesheetSet. We don't bother creating a constructor, because
|
/// Entry for a StylesheetSet. We don't bother creating a constructor, because
|
||||||
/// there's no sensible defaults for the member variables.
|
/// there's no sensible defaults for the member variables.
|
||||||
|
@ -40,6 +44,9 @@ pub struct StylesheetSet {
|
||||||
|
|
||||||
/// Has author style been disabled?
|
/// Has author style been disabled?
|
||||||
author_style_disabled: bool,
|
author_style_disabled: bool,
|
||||||
|
|
||||||
|
/// The style invalidations that we still haven't processed.
|
||||||
|
invalidations: StylesheetInvalidationSet,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StylesheetSet {
|
impl StylesheetSet {
|
||||||
|
@ -49,6 +56,7 @@ impl StylesheetSet {
|
||||||
entries: vec![],
|
entries: vec![],
|
||||||
dirty: false,
|
dirty: false,
|
||||||
author_style_disabled: false,
|
author_style_disabled: false,
|
||||||
|
invalidations: StylesheetInvalidationSet::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,32 +71,57 @@ impl StylesheetSet {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Appends a new stylesheet to the current set.
|
/// Appends a new stylesheet to the current set.
|
||||||
pub fn append_stylesheet(&mut self, sheet: &Arc<Stylesheet>,
|
pub fn append_stylesheet(
|
||||||
unique_id: u64) {
|
&mut self,
|
||||||
|
stylist: &Stylist,
|
||||||
|
sheet: &Arc<Stylesheet>,
|
||||||
|
unique_id: u64,
|
||||||
|
guard: &SharedRwLockReadGuard)
|
||||||
|
{
|
||||||
|
debug!("StylesheetSet::append_stylesheet");
|
||||||
self.remove_stylesheet_if_present(unique_id);
|
self.remove_stylesheet_if_present(unique_id);
|
||||||
self.entries.push(StylesheetSetEntry {
|
self.entries.push(StylesheetSetEntry {
|
||||||
unique_id: unique_id,
|
unique_id: unique_id,
|
||||||
sheet: sheet.clone(),
|
sheet: sheet.clone(),
|
||||||
});
|
});
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
self.invalidations.collect_invalidations_for(
|
||||||
|
stylist,
|
||||||
|
sheet,
|
||||||
|
guard)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prepend a new stylesheet to the current set.
|
/// Prepend a new stylesheet to the current set.
|
||||||
pub fn prepend_stylesheet(&mut self, sheet: &Arc<Stylesheet>,
|
pub fn prepend_stylesheet(
|
||||||
unique_id: u64) {
|
&mut self,
|
||||||
|
stylist: &Stylist,
|
||||||
|
sheet: &Arc<Stylesheet>,
|
||||||
|
unique_id: u64,
|
||||||
|
guard: &SharedRwLockReadGuard)
|
||||||
|
{
|
||||||
|
debug!("StylesheetSet::prepend_stylesheet");
|
||||||
self.remove_stylesheet_if_present(unique_id);
|
self.remove_stylesheet_if_present(unique_id);
|
||||||
self.entries.insert(0, StylesheetSetEntry {
|
self.entries.insert(0, StylesheetSetEntry {
|
||||||
unique_id: unique_id,
|
unique_id: unique_id,
|
||||||
sheet: sheet.clone(),
|
sheet: sheet.clone(),
|
||||||
});
|
});
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
self.invalidations.collect_invalidations_for(
|
||||||
|
stylist,
|
||||||
|
sheet,
|
||||||
|
guard)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a given stylesheet before another stylesheet in the document.
|
/// Insert a given stylesheet before another stylesheet in the document.
|
||||||
pub fn insert_stylesheet_before(&mut self,
|
pub fn insert_stylesheet_before(
|
||||||
|
&mut self,
|
||||||
|
stylist: &Stylist,
|
||||||
sheet: &Arc<Stylesheet>,
|
sheet: &Arc<Stylesheet>,
|
||||||
unique_id: u64,
|
unique_id: u64,
|
||||||
before_unique_id: u64) {
|
before_unique_id: u64,
|
||||||
|
guard: &SharedRwLockReadGuard)
|
||||||
|
{
|
||||||
|
debug!("StylesheetSet::insert_stylesheet_before");
|
||||||
self.remove_stylesheet_if_present(unique_id);
|
self.remove_stylesheet_if_present(unique_id);
|
||||||
let index = self.entries.iter().position(|x| {
|
let index = self.entries.iter().position(|x| {
|
||||||
x.unique_id == before_unique_id
|
x.unique_id == before_unique_id
|
||||||
|
@ -98,21 +131,30 @@ impl StylesheetSet {
|
||||||
sheet: sheet.clone(),
|
sheet: sheet.clone(),
|
||||||
});
|
});
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
self.invalidations.collect_invalidations_for(
|
||||||
|
stylist,
|
||||||
|
sheet,
|
||||||
|
guard)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove a given stylesheet from the set.
|
/// Remove a given stylesheet from the set.
|
||||||
pub fn remove_stylesheet(&mut self, unique_id: u64) {
|
pub fn remove_stylesheet(&mut self, unique_id: u64) {
|
||||||
|
debug!("StylesheetSet::remove_stylesheet");
|
||||||
self.remove_stylesheet_if_present(unique_id);
|
self.remove_stylesheet_if_present(unique_id);
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
// FIXME(emilio): We can do better!
|
||||||
|
self.invalidations.invalidate_fully();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Notes that the author style has been disabled for this document.
|
/// Notes that the author style has been disabled for this document.
|
||||||
pub fn set_author_style_disabled(&mut self, disabled: bool) {
|
pub fn set_author_style_disabled(&mut self, disabled: bool) {
|
||||||
|
debug!("StylesheetSet::set_author_style_disabled");
|
||||||
if self.author_style_disabled == disabled {
|
if self.author_style_disabled == disabled {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.author_style_disabled = disabled;
|
self.author_style_disabled = disabled;
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
self.invalidations.invalidate_fully();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether the given set has changed from the last flush.
|
/// 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
|
/// Flush the current set, unmarking it as dirty, and returns an iterator
|
||||||
/// over the new stylesheet list.
|
/// 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.dirty = false;
|
||||||
|
self.invalidations.flush(document_element);
|
||||||
|
|
||||||
StylesheetIterator(self.entries.iter())
|
StylesheetIterator(self.entries.iter())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,5 +184,6 @@ impl StylesheetSet {
|
||||||
/// FIXME(emilio): Make this more granular.
|
/// FIXME(emilio): Make this more granular.
|
||||||
pub fn force_dirty(&mut self) {
|
pub fn force_dirty(&mut self) {
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
self.invalidations.invalidate_fully();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,11 +37,13 @@ use servo_config::prefs::PREFS;
|
||||||
#[cfg(not(feature = "gecko"))]
|
#[cfg(not(feature = "gecko"))]
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
use shared_lock::{SharedRwLock, Locked, ToCssWithGuard, SharedRwLockReadGuard};
|
use shared_lock::{SharedRwLock, Locked, ToCssWithGuard, SharedRwLockReadGuard};
|
||||||
|
use smallvec::SmallVec;
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::mem::align_of;
|
use std::mem::align_of;
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
|
use std::slice;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use str::starts_with_ignore_ascii_case;
|
use str::starts_with_ignore_ascii_case;
|
||||||
use style_traits::ToCss;
|
use style_traits::ToCss;
|
||||||
|
@ -432,16 +434,6 @@ pub enum CssRuleType {
|
||||||
Viewport = 15,
|
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)]
|
#[allow(missing_docs)]
|
||||||
pub enum SingleRuleParseError {
|
pub enum SingleRuleParseError {
|
||||||
Syntax,
|
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
|
// input state is None for a nested rule
|
||||||
// Returns a parsed CSS rule and the final state of the parser
|
// Returns a parsed CSS rule and the final state of the parser
|
||||||
#[allow(missing_docs)]
|
#[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 {
|
impl Stylesheet {
|
||||||
/// Updates an empty stylesheet from a given string of text.
|
/// Updates an empty stylesheet from a given string of text.
|
||||||
pub fn update_from_str(existing: &Stylesheet,
|
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
|
/// Return an iterator over the effective rules within the style-sheet, as
|
||||||
/// according to the supplied `Device`.
|
/// 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]
|
#[inline]
|
||||||
pub fn effective_rules<F>(&self, device: &Device, guard: &SharedRwLockReadGuard, mut f: F)
|
pub fn effective_rules<'a, 'b>(
|
||||||
where F: FnMut(&CssRule) {
|
&'a self,
|
||||||
effective_rules(&self.rules.read_with(guard).0, device, self.quirks_mode, guard, &mut f);
|
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
|
/// 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 {
|
macro_rules! rule_filter {
|
||||||
($( $method: ident($variant:ident => $rule_type: ident), )+) => {
|
($( $method: ident($variant:ident => $rule_type: ident), )+) => {
|
||||||
impl Stylesheet {
|
impl Stylesheet {
|
||||||
$(
|
$(
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub fn $method<F>(&self, device: &Device, guard: &SharedRwLockReadGuard, mut f: F)
|
pub fn $method<F>(&self, device: &Device, guard: &SharedRwLockReadGuard, mut f: F)
|
||||||
where F: FnMut(&$rule_type) {
|
where F: FnMut(&$rule_type),
|
||||||
self.effective_rules(device, guard, |rule| {
|
{
|
||||||
|
for rule in self.effective_rules(device, guard) {
|
||||||
if let CssRule::$variant(ref lock) = *rule {
|
if let CssRule::$variant(ref lock) = *rule {
|
||||||
let rule = lock.read_with(guard);
|
let rule = lock.read_with(guard);
|
||||||
f(&rule)
|
f(&rule)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
)+
|
)+
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,10 +39,9 @@ use style_traits::viewport::ViewportConstraints;
|
||||||
use stylearc::Arc;
|
use stylearc::Arc;
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
use stylesheets::{CounterStyleRule, FontFaceRule};
|
use stylesheets::{CounterStyleRule, FontFaceRule};
|
||||||
use stylesheets::{CssRule, Origin};
|
use stylesheets::{CssRule, DocumentRule, ImportRule, MediaRule, StyleRule, SupportsRule};
|
||||||
use stylesheets::{StyleRule, Stylesheet, UserAgentStylesheets};
|
use stylesheets::{Stylesheet, Origin, UserAgentStylesheets};
|
||||||
#[cfg(feature = "servo")]
|
use stylesheets::NestedRuleIterationCondition;
|
||||||
use stylesheets::NestedRulesResult;
|
|
||||||
use thread_state;
|
use thread_state;
|
||||||
use viewport::{self, MaybeNew, ViewportRule};
|
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
|
/// On Servo, on the other hand, the device is a really cheap representation
|
||||||
/// that is recreated each time some constraint changes and calling
|
/// that is recreated each time some constraint changes and calling
|
||||||
/// `set_device`.
|
/// `set_device`.
|
||||||
///
|
device: 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>,
|
|
||||||
|
|
||||||
/// Viewport constraints based on the current device.
|
/// Viewport constraints based on the current device.
|
||||||
viewport_constraints: Option<ViewportConstraints>,
|
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 {
|
impl Stylist {
|
||||||
/// Construct a new `Stylist`, using given `Device` and `QuirksMode`.
|
/// Construct a new `Stylist`, using given `Device` and `QuirksMode`.
|
||||||
/// If more members are added here, think about whether they should
|
/// 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 {
|
pub fn new(device: Device, quirks_mode: QuirksMode) -> Self {
|
||||||
let mut stylist = Stylist {
|
let mut stylist = Stylist {
|
||||||
viewport_constraints: None,
|
viewport_constraints: None,
|
||||||
device: Arc::new(device),
|
device: device,
|
||||||
is_device_dirty: true,
|
is_device_dirty: true,
|
||||||
is_cleared: true,
|
is_cleared: true,
|
||||||
quirks_mode: quirks_mode,
|
quirks_mode: quirks_mode,
|
||||||
|
@ -364,8 +411,7 @@ impl Stylist {
|
||||||
ViewportConstraints::maybe_new(&self.device, &cascaded_rule, self.quirks_mode);
|
ViewportConstraints::maybe_new(&self.device, &cascaded_rule, self.quirks_mode);
|
||||||
|
|
||||||
if let Some(ref constraints) = self.viewport_constraints {
|
if let Some(ref constraints) = self.viewport_constraints {
|
||||||
Arc::get_mut(&mut self.device).unwrap()
|
self.device.account_for_viewport_rule(constraints);
|
||||||
.account_for_viewport_rule(constraints);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
|
SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
|
||||||
|
@ -431,31 +477,47 @@ impl Stylist {
|
||||||
fn add_stylesheet<'a>(&mut self,
|
fn add_stylesheet<'a>(&mut self,
|
||||||
stylesheet: &Stylesheet,
|
stylesheet: &Stylesheet,
|
||||||
guard: &SharedRwLockReadGuard,
|
guard: &SharedRwLockReadGuard,
|
||||||
extra_data: &mut ExtraStyleData<'a>) {
|
_extra_data: &mut ExtraStyleData<'a>) {
|
||||||
if stylesheet.disabled() || !stylesheet.is_effective_for_device(&self.device, guard) {
|
if stylesheet.disabled() || !stylesheet.is_effective_for_device(&self.device, guard) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cheap `Arc` clone so that the closure below can borrow `&mut Stylist`.
|
for rule in stylesheet.effective_rules(&self.device, guard) {
|
||||||
let device = self.device.clone();
|
|
||||||
|
|
||||||
stylesheet.effective_rules(&device, guard, |rule| {
|
|
||||||
match *rule {
|
match *rule {
|
||||||
CssRule::Style(ref locked) => {
|
CssRule::Style(ref locked) => {
|
||||||
let style_rule = locked.read_with(&guard);
|
let style_rule = locked.read_with(&guard);
|
||||||
self.num_declarations += style_rule.block.read_with(&guard).len();
|
self.num_declarations += style_rule.block.read_with(&guard).len();
|
||||||
for selector in &style_rule.selectors.0 {
|
for selector in &style_rule.selectors.0 {
|
||||||
self.num_selectors += 1;
|
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.dependencies.note_selector(selector);
|
||||||
self.note_for_revalidation(selector);
|
if needs_revalidation(selector) {
|
||||||
self.note_attribute_and_state_dependencies(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;
|
self.rules_source_order += 1;
|
||||||
}
|
}
|
||||||
CssRule::Import(ref import) => {
|
CssRule::Import(..) => {
|
||||||
let import = import.read_with(guard);
|
// effective_rules visits the inner stylesheet if
|
||||||
self.add_stylesheet(&import.stylesheet, guard, extra_data)
|
// appropriate.
|
||||||
}
|
}
|
||||||
CssRule::Keyframes(ref keyframes_rule) => {
|
CssRule::Keyframes(ref keyframes_rule) => {
|
||||||
let keyframes_rule = keyframes_rule.read_with(guard);
|
let keyframes_rule = keyframes_rule.read_with(guard);
|
||||||
|
@ -474,42 +536,15 @@ impl Stylist {
|
||||||
}
|
}
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
CssRule::FontFace(ref rule) => {
|
CssRule::FontFace(ref rule) => {
|
||||||
extra_data.add_font_face(&rule, stylesheet.origin);
|
_extra_data.add_font_face(&rule, stylesheet.origin);
|
||||||
}
|
}
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
CssRule::CounterStyle(ref rule) => {
|
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.
|
// 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,
|
pub fn might_have_attribute_dependency(&self,
|
||||||
local_name: &LocalName)
|
local_name: &LocalName)
|
||||||
-> bool {
|
-> bool {
|
||||||
#[cfg(feature = "servo")]
|
if *local_name == local_name!("style") {
|
||||||
let style_lower_name = local_name!("style");
|
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
let style_lower_name = atom!("style");
|
|
||||||
|
|
||||||
if *local_name == style_lower_name {
|
|
||||||
self.style_attribute_dependency
|
self.style_attribute_dependency
|
||||||
} else {
|
} else {
|
||||||
self.attribute_dependencies.might_contain(local_name)
|
self.attribute_dependencies.might_contain(local_name)
|
||||||
|
@ -536,11 +566,6 @@ impl Stylist {
|
||||||
self.state_dependencies.intersects(state)
|
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
|
/// Computes the style for a given "precomputed" pseudo-element, taking the
|
||||||
/// universal rules and applying them.
|
/// universal rules and applying them.
|
||||||
///
|
///
|
||||||
|
@ -791,44 +816,62 @@ impl Stylist {
|
||||||
device.account_for_viewport_rule(constraints);
|
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| {
|
self.is_device_dirty |= stylesheets.iter().any(|stylesheet| {
|
||||||
let mq = stylesheet.media.read_with(guard);
|
let mq = stylesheet.media.read_with(guard);
|
||||||
if mq.evaluate(&self.device, self.quirks_mode) != mq.evaluate(&device, self.quirks_mode) {
|
if mq.evaluate(&self.device, self.quirks_mode) != mq.evaluate(&device, self.quirks_mode) {
|
||||||
return true
|
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
|
/// 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.
|
/// 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
|
&mut self.device
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1146,7 +1189,11 @@ impl Drop for Stylist {
|
||||||
|
|
||||||
/// Visitor to collect names that appear in attribute selectors and any
|
/// Visitor to collect names that appear in attribute selectors and any
|
||||||
/// dependencies on ElementState bits.
|
/// 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> {
|
impl<'a> SelectorVisitor for AttributeAndStateDependencyVisitor<'a> {
|
||||||
type Impl = SelectorImpl;
|
type Impl = SelectorImpl;
|
||||||
|
@ -1160,17 +1207,17 @@ impl<'a> SelectorVisitor for AttributeAndStateDependencyVisitor<'a> {
|
||||||
let style_lower_name = atom!("style");
|
let style_lower_name = atom!("style");
|
||||||
|
|
||||||
if *lower_name == style_lower_name {
|
if *lower_name == style_lower_name {
|
||||||
self.0.style_attribute_dependency = true;
|
*self.style_attribute_dependency = true;
|
||||||
} else {
|
} else {
|
||||||
self.0.attribute_dependencies.insert(&name);
|
self.attribute_dependencies.insert(&name);
|
||||||
self.0.attribute_dependencies.insert(&lower_name);
|
self.attribute_dependencies.insert(&lower_name);
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_simple_selector(&mut self, s: &Component<SelectorImpl>) -> bool {
|
fn visit_simple_selector(&mut self, s: &Component<SelectorImpl>) -> bool {
|
||||||
if let Component::NonTSPseudoClass(ref p) = *s {
|
if let Component::NonTSPseudoClass(ref p) = *s {
|
||||||
self.0.state_dependencies.insert(p.state_flag());
|
self.state_dependencies.insert(p.state_flag());
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ use style::gecko::selector_parser::PseudoElement;
|
||||||
use style::gecko::traversal::RecalcStyleOnly;
|
use style::gecko::traversal::RecalcStyleOnly;
|
||||||
use style::gecko::wrapper::GeckoElement;
|
use style::gecko::wrapper::GeckoElement;
|
||||||
use style::gecko_bindings::bindings;
|
use style::gecko_bindings::bindings;
|
||||||
|
use style::gecko_bindings::bindings::{RawGeckoElementBorrowed, RawGeckoElementBorrowedOrNull};
|
||||||
use style::gecko_bindings::bindings::{RawGeckoKeyframeListBorrowed, RawGeckoKeyframeListBorrowedMut};
|
use style::gecko_bindings::bindings::{RawGeckoKeyframeListBorrowed, RawGeckoKeyframeListBorrowedMut};
|
||||||
use style::gecko_bindings::bindings::{RawServoDeclarationBlockBorrowed, RawServoDeclarationBlockStrong};
|
use style::gecko_bindings::bindings::{RawServoDeclarationBlockBorrowed, RawServoDeclarationBlockStrong};
|
||||||
use style::gecko_bindings::bindings::{RawServoDocumentRule, RawServoDocumentRuleBorrowed};
|
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::RawGeckoCSSPropertyIDListBorrowed;
|
||||||
use style::gecko_bindings::bindings::RawGeckoComputedKeyframeValuesListBorrowedMut;
|
use style::gecko_bindings::bindings::RawGeckoComputedKeyframeValuesListBorrowedMut;
|
||||||
use style::gecko_bindings::bindings::RawGeckoComputedTimingBorrowed;
|
use style::gecko_bindings::bindings::RawGeckoComputedTimingBorrowed;
|
||||||
use style::gecko_bindings::bindings::RawGeckoElementBorrowed;
|
|
||||||
use style::gecko_bindings::bindings::RawGeckoFontFaceRuleListBorrowedMut;
|
use style::gecko_bindings::bindings::RawGeckoFontFaceRuleListBorrowedMut;
|
||||||
use style::gecko_bindings::bindings::RawGeckoServoStyleRuleListBorrowedMut;
|
use style::gecko_bindings::bindings::RawGeckoServoStyleRuleListBorrowedMut;
|
||||||
use style::gecko_bindings::bindings::RawServoAnimationValueBorrowed;
|
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,
|
pub extern "C" fn Servo_StyleSet_AppendStyleSheet(raw_data: RawServoStyleSetBorrowed,
|
||||||
raw_sheet: RawServoStyleSheetBorrowed,
|
raw_sheet: RawServoStyleSheetBorrowed,
|
||||||
unique_id: u64) {
|
unique_id: u64) {
|
||||||
|
let global_style_data = &*GLOBAL_STYLE_DATA;
|
||||||
let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
|
let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
|
||||||
|
let mut data = &mut *data;
|
||||||
let sheet = HasArcFFI::as_arc(&raw_sheet);
|
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();
|
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,
|
pub extern "C" fn Servo_StyleSet_PrependStyleSheet(raw_data: RawServoStyleSetBorrowed,
|
||||||
raw_sheet: RawServoStyleSheetBorrowed,
|
raw_sheet: RawServoStyleSheetBorrowed,
|
||||||
unique_id: u64) {
|
unique_id: u64) {
|
||||||
|
let global_style_data = &*GLOBAL_STYLE_DATA;
|
||||||
let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
|
let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
|
||||||
|
let mut data = &mut *data;
|
||||||
let sheet = HasArcFFI::as_arc(&raw_sheet);
|
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();
|
data.clear_stylist();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -753,9 +767,17 @@ pub extern "C" fn Servo_StyleSet_InsertStyleSheetBefore(raw_data: RawServoStyleS
|
||||||
raw_sheet: RawServoStyleSheetBorrowed,
|
raw_sheet: RawServoStyleSheetBorrowed,
|
||||||
unique_id: u64,
|
unique_id: u64,
|
||||||
before_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 = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
|
||||||
|
let mut data = &mut *data;
|
||||||
let sheet = HasArcFFI::as_arc(&raw_sheet);
|
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();
|
data.clear_stylist();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -768,16 +790,22 @@ pub extern "C" fn Servo_StyleSet_RemoveStyleSheet(raw_data: RawServoStyleSetBorr
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[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 global_style_data = &*GLOBAL_STYLE_DATA;
|
||||||
let guard = global_style_data.shared_lock.read();
|
let guard = global_style_data.shared_lock.read();
|
||||||
let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
|
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]
|
#[no_mangle]
|
||||||
pub extern "C" fn Servo_StyleSet_NoteStyleSheetsChanged(raw_data: RawServoStyleSetBorrowed,
|
pub extern "C" fn Servo_StyleSet_NoteStyleSheetsChanged(
|
||||||
author_style_disabled: bool) {
|
raw_data: RawServoStyleSetBorrowed,
|
||||||
|
author_style_disabled: bool)
|
||||||
|
{
|
||||||
let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
|
let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
|
||||||
data.stylesheets.force_dirty();
|
data.stylesheets.force_dirty();
|
||||||
data.stylesheets.set_author_style_disabled(author_style_disabled);
|
data.stylesheets.set_author_style_disabled(author_style_disabled);
|
||||||
|
|
|
@ -13,7 +13,7 @@ use style::media_queries::*;
|
||||||
use style::servo::media_queries::*;
|
use style::servo::media_queries::*;
|
||||||
use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard};
|
use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard};
|
||||||
use style::stylearc::Arc;
|
use style::stylearc::Arc;
|
||||||
use style::stylesheets::{Stylesheet, Origin, CssRule, NestedRulesResult};
|
use style::stylesheets::{AllRules, Stylesheet, Origin, CssRule};
|
||||||
use style::values::specified;
|
use style::values::specified;
|
||||||
use style_traits::ToCss;
|
use style_traits::ToCss;
|
||||||
|
|
||||||
|
@ -39,33 +39,18 @@ fn test_media_rule<F>(css: &str, callback: F)
|
||||||
let stylesheet = Stylesheet::from_str(
|
let stylesheet = Stylesheet::from_str(
|
||||||
css, url, Origin::Author, media_list, lock,
|
css, url, Origin::Author, media_list, lock,
|
||||||
None, &CSSErrorReporterTest, QuirksMode::NoQuirks, 0u64);
|
None, &CSSErrorReporterTest, QuirksMode::NoQuirks, 0u64);
|
||||||
|
let dummy = Device::new(MediaType::Screen, TypedSize2D::new(200.0, 100.0));
|
||||||
let mut rule_count = 0;
|
let mut rule_count = 0;
|
||||||
let guard = stylesheet.shared_lock.read();
|
let guard = stylesheet.shared_lock.read();
|
||||||
media_queries(&guard, &stylesheet.rules.read_with(&guard).0, &mut |mq| {
|
for rule in stylesheet.iter_rules::<AllRules>(&dummy, &guard) {
|
||||||
|
if let CssRule::Media(ref lock) = *rule {
|
||||||
rule_count += 1;
|
rule_count += 1;
|
||||||
callback(mq, css);
|
callback(&lock.read_with(&guard).media_queries.read_with(&guard), css);
|
||||||
});
|
}
|
||||||
|
}
|
||||||
assert!(rule_count > 0, css_str);
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn media_query_test(device: &Device, css: &str, expected_rule_count: usize) {
|
fn media_query_test(device: &Device, css: &str, expected_rule_count: usize) {
|
||||||
let url = ServoUrl::parse("http://localhost").unwrap();
|
let url = ServoUrl::parse("http://localhost").unwrap();
|
||||||
let lock = SharedRwLock::new();
|
let lock = SharedRwLock::new();
|
||||||
|
|
Загрузка…
Ссылка в новой задаче