diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs index a2f7b7ca81e..bffadf0a551 100644 --- a/components/layout/layout_task.rs +++ b/components/layout/layout_task.rs @@ -66,7 +66,7 @@ use style::computed_values::{filter, mix_blend_mode}; use style::media_queries::{MediaType, MediaQueryList, Device}; use style::node::TNode; use style::selector_matching::Stylist; -use style::stylesheets::{Origin, Stylesheet, iter_font_face_rules}; +use style::stylesheets::{Origin, Stylesheet, CSSRuleIteratorExt}; use url::Url; use util::cursor::Cursor; use util::geometry::{Au, MAX_RECT}; @@ -603,9 +603,11 @@ impl LayoutTask { let mut rw_data = self.lock_rw_data(possibly_locked_rw_data); if mq.evaluate(&rw_data.stylist.device) { - iter_font_face_rules(&sheet, &rw_data.stylist.device, &|family, src| { - self.font_cache_task.add_web_font((*family).clone(), (*src).clone()); - }); + for font_face in sheet.effective_rules(&rw_data.stylist.device).font_face() { + for source in font_face.sources.iter() { + self.font_cache_task.add_web_font(font_face.family.clone(), source.clone()); + } + } rw_data.stylist.add_stylesheet(sheet); } diff --git a/components/style/font_face.rs b/components/style/font_face.rs index 0c661745fb5..15328d98f61 100644 --- a/components/style/font_face.rs +++ b/components/style/font_face.rs @@ -4,33 +4,12 @@ use computed_values::font_family::FontFamily; use cssparser::{Token, Parser, DeclarationListParser, AtRuleParser, DeclarationParser}; -use media_queries::Device; use parser::{ParserContext, log_css_error}; use properties::longhands::font_family::parse_one_family; use std::ascii::AsciiExt; use string_cache::Atom; -use stylesheets::CSSRule; use url::{Url, UrlParser}; -pub fn iter_font_face_rules_inner(rules: &[CSSRule], device: &Device, callback: &F) - where F: Fn(&Atom, &Source) { - for rule in rules.iter() { - match *rule { - CSSRule::Style(..) | - CSSRule::Charset(..) | - CSSRule::Namespace(..) => {}, - CSSRule::Media(ref rule) => if rule.media_queries.evaluate(device) { - iter_font_face_rules_inner(&rule.rules, device, callback) - }, - CSSRule::FontFace(ref rule) => { - for source in rule.sources.iter() { - callback(&rule.family, source) - } - }, - } - } -} - #[derive(Clone, Debug, PartialEq, Eq)] pub enum Source { Url(UrlSource), diff --git a/components/style/selector_matching.rs b/components/style/selector_matching.rs index a40fed2bd5b..6511f8c5772 100644 --- a/components/style/selector_matching.rs +++ b/components/style/selector_matching.rs @@ -16,7 +16,7 @@ use legacy::PresentationalHintSynthesis; use media_queries::Device; use node::TElementAttributes; use properties::{PropertyDeclaration, PropertyDeclarationBlock}; -use stylesheets::{Stylesheet, iter_stylesheet_media_rules, iter_stylesheet_style_rules, Origin}; +use stylesheets::{Stylesheet, CSSRuleIteratorExt, Origin}; pub type DeclarationBlock = GenericDeclarationBlock>; @@ -120,11 +120,11 @@ impl Stylist { }; ); - iter_stylesheet_style_rules(stylesheet, &self.device, |style_rule| { + for style_rule in stylesheet.effective_rules(&self.device).style() { append!(style_rule, normal); append!(style_rule, important); rules_source_order += 1; - }); + } self.rules_source_order = rules_source_order; } @@ -136,14 +136,9 @@ impl Stylist { } pub fn set_device(&mut self, device: Device) { - let is_dirty = self.is_dirty || self.stylesheets.iter().any(|stylesheet| { - let mut stylesheet_dirty = false; - iter_stylesheet_media_rules(stylesheet, |rule| { - stylesheet_dirty |= rule.media_queries.evaluate(&self.device) != - rule.media_queries.evaluate(&device); - }); - stylesheet_dirty - }); + let is_dirty = self.is_dirty || self.stylesheets.iter() + .flat_map(|stylesheet| stylesheet.rules().media()) + .any(|media_rule| media_rule.evaluate(&self.device) != media_rule.evaluate(&device)); self.device = device; self.is_dirty |= is_dirty; @@ -280,4 +275,3 @@ impl PerPseudoElementSelectorMap { } } } - diff --git a/components/style/stylesheets.rs b/components/style/stylesheets.rs index 1bc41a20da1..50cc34ed9fa 100644 --- a/components/style/stylesheets.rs +++ b/components/style/stylesheets.rs @@ -2,9 +2,10 @@ * 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/. */ +use std::ascii::AsciiExt; use std::cell::Cell; use std::iter::Iterator; -use std::ascii::AsciiExt; +use std::slice; use url::Url; use encoding::EncodingRef; @@ -15,8 +16,9 @@ use string_cache::{Atom, Namespace}; use selectors::parser::{Selector, parse_selector_list}; use parser::{ParserContext, log_css_error}; use properties::{PropertyDeclarationBlock, parse_property_declaration_list}; -use media_queries::{self, Device, MediaQueryList, parse_media_query_list}; -use font_face::{FontFaceRule, Source, parse_font_face_block, iter_font_face_rules_inner}; +use media_queries::{Device, MediaQueryList, parse_media_query_list}; +use font_face::{FontFaceRule, parse_font_face_block}; +use util::smallvec::{SmallVec, SmallVec2}; #[derive(Clone, PartialEq, Eq, Copy, Debug)] @@ -51,6 +53,12 @@ pub struct MediaRule { pub rules: Vec, } +impl MediaRule { + #[inline] + pub fn evaluate(&self, device: &Device) -> bool { + self.media_queries.evaluate(device) + } +} #[derive(Debug, PartialEq)] pub struct StyleRule { @@ -118,8 +126,161 @@ impl Stylesheet { rules: rules, } } + + /// Return an iterator over all the rules within the style-sheet. + #[inline] + pub fn rules<'a>(&'a self) -> Rules<'a> { + Rules::new(self.rules.iter(), None) + } + + /// 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<'a>(&'a self, device: &'a Device) -> Rules<'a> { + Rules::new(self.rules.iter(), Some(device)) + } } +/// `CSSRule` iterator. +/// +/// The iteration order is pre-order. Specifically, this implies that a +/// conditional group rule will come before its nested rules. +pub struct Rules<'a> { + // 2 because normal case is likely to be just one level of nesting (@media) + stack: SmallVec2>, + device: Option<&'a Device> +} + +impl<'a> Rules<'a> { + fn new(iter: slice::Iter<'a, CSSRule>, device: Option<&'a Device>) -> Rules<'a> { + let mut stack = SmallVec2::new(); + stack.push(iter); + + Rules { stack: stack, device: device } + } +} + +impl<'a> Iterator for Rules<'a> { + type Item = &'a CSSRule; + + fn next(&mut self) -> Option<&'a CSSRule> { + while !self.stack.is_empty() { + let top = self.stack.len() - 1; + while let Some(rule) = self.stack.get_mut(top).next() { + // handle conditional group rules + match rule { + &CSSRule::Media(ref rule) => { + if let Some(device) = self.device { + if rule.evaluate(device) { + self.stack.push(rule.rules.iter()); + } else { + continue + } + } else { + self.stack.push(rule.rules.iter()); + } + } + _ => {} + } + + return Some(rule) + } + + self.stack.pop(); + } + None + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + // TODO: track total number of rules in style-sheet for upper bound? + (0, None) + } +} + +pub mod rule_filter { + //! Specific `CSSRule` variant iterators. + + use std::marker::PhantomData; + use super::{CSSRule, MediaRule, StyleRule}; + use super::super::font_face::FontFaceRule; + + macro_rules! rule_filter { + ($variant:ident -> $value:ty) => { + /// An iterator that only yields rules that are of the synonymous `CSSRule` variant. + #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] + pub struct $variant<'a, I> { + iter: I, + _lifetime: PhantomData<&'a ()> + } + + impl<'a, I> $variant<'a, I> where I: Iterator { + pub fn new(iter: I) -> $variant<'a, I> { + $variant { + iter: iter, + _lifetime: PhantomData + } + } + } + + impl<'a, I> Iterator for $variant<'a, I> where I: Iterator { + type Item = &'a $value; + + fn next(&mut self) -> Option<&'a $value> { + while let Some(rule) = self.iter.next() { + match rule { + &CSSRule::$variant(ref value) => return Some(value), + _ => continue + } + } + None + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (0, self.iter.size_hint().1) + } + } + } + } + + rule_filter!(FontFace -> FontFaceRule); + rule_filter!(Media -> MediaRule); + rule_filter!(Style -> StyleRule); +} + +/// Extension methods for `CSSRule` iterators. +pub trait CSSRuleIteratorExt<'a>: Iterator { + /// Yield only @font-face rules. + fn font_face(self) -> rule_filter::FontFace<'a, Self>; + + /// Yield only @media rules. + fn media(self) -> rule_filter::Media<'a, Self>; + + /// Yield only style rules. + fn style(self) -> rule_filter::Style<'a, Self>; +} + +impl<'a, I> CSSRuleIteratorExt<'a> for I where I: Iterator { + #[inline] + fn font_face(self) -> rule_filter::FontFace<'a, I> { + rule_filter::FontFace::new(self) + } + + #[inline] + fn media(self) -> rule_filter::Media<'a, I> { + rule_filter::Media::new(self) + } + + #[inline] + fn style(self) -> rule_filter::Style<'a, I> { + rule_filter::Style::new(self) + } +} fn parse_nested_rules(context: &ParserContext, input: &mut Parser) -> Vec { let mut iter = RuleListParser::new_for_nested_rule(input, NestedRuleParser { context: context }); @@ -281,44 +442,3 @@ impl<'a, 'b> QualifiedRuleParser for NestedRuleParser<'a, 'b> { })) } } - - -pub fn iter_style_rules<'a, F>(rules: &[CSSRule], device: &media_queries::Device, - callback: &mut F) where F: FnMut(&StyleRule) { - for rule in rules.iter() { - match *rule { - CSSRule::Style(ref rule) => callback(rule), - CSSRule::Media(ref rule) => if rule.media_queries.evaluate(device) { - iter_style_rules(&rule.rules, device, callback) - }, - CSSRule::FontFace(..) | - CSSRule::Charset(..) | - CSSRule::Namespace(..) => {} - } - } -} - -pub fn iter_stylesheet_media_rules(stylesheet: &Stylesheet, mut callback: F) where F: FnMut(&MediaRule) { - for rule in stylesheet.rules.iter() { - match *rule { - CSSRule::Media(ref rule) => callback(rule), - CSSRule::Style(..) | - CSSRule::FontFace(..) | - CSSRule::Charset(..) | - CSSRule::Namespace(..) => {} - } - } -} - -#[inline] -pub fn iter_stylesheet_style_rules(stylesheet: &Stylesheet, device: &media_queries::Device, - mut callback: F) where F: FnMut(&StyleRule) { - iter_style_rules(&stylesheet.rules, device, &mut callback) -} - - -#[inline] -pub fn iter_font_face_rules(stylesheet: &Stylesheet, device: &Device, callback: &F) - where F: Fn(&Atom, &Source) { - iter_font_face_rules_inner(&stylesheet.rules, device, callback) -}