From 444fef164ed172de18fc6f2daf47c494fb4aae1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Fri, 16 Dec 2016 12:05:19 +0100 Subject: [PATCH] style: Add a struct to represent import rules, and parse them correctly. --- components/style/stylesheets.rs | 76 ++++++++++++++++++++++-- components/style/stylist.rs | 24 ++++++++ components/style/values/specified/url.rs | 10 +++- 3 files changed, 103 insertions(+), 7 deletions(-) diff --git a/components/style/stylesheets.rs b/components/style/stylesheets.rs index 247c153c46b..37dd578f3ad 100644 --- a/components/style/stylesheets.rs +++ b/components/style/stylesheets.rs @@ -26,6 +26,7 @@ use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use style_traits::ToCss; use stylist::FnvHashMap; +use values::specified::url::SpecifiedUrl; use viewport::ViewportRule; @@ -79,7 +80,8 @@ impl CssRules { fn only_ns_or_import(&self) -> bool { self.0.iter().all(|r| { match *r { - CssRule::Namespace(..) /* | CssRule::Import(..) */ => true, + CssRule::Namespace(..) | + CssRule::Import(..) => true, _ => false } }) @@ -180,6 +182,7 @@ pub enum CssRule { // https://drafts.csswg.org/cssom/#changes-from-5-december-2013 Namespace(Arc>), + Import(Arc>), Style(Arc>), Media(Arc>), FontFace(Arc>), @@ -235,6 +238,7 @@ impl CssRule { pub fn rule_type(&self) -> CssRuleType { match *self { CssRule::Style(_) => CssRuleType::Style, + CssRule::Import(_) => CssRuleType::Import, CssRule::Media(_) => CssRuleType::Media, CssRule::FontFace(_) => CssRuleType::FontFace, CssRule::Keyframes(_) => CssRuleType::Keyframes, @@ -246,7 +250,7 @@ impl CssRule { fn rule_state(&self) -> State { match *self { // CssRule::Charset(..) => State::Start, - // CssRule::Import(..) => State::Imports, + CssRule::Import(..) => State::Imports, CssRule::Namespace(..) => State::Namespaces, _ => State::Body, } @@ -254,10 +258,19 @@ 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. + /// Note that only some types of rules can contain rules. An empty slice is + /// used for others. pub fn with_nested_rules_and_mq(&self, mut f: F) -> R where F: FnMut(&[CssRule], Option<&MediaList>) -> R { match *self { + CssRule::Import(ref lock) => { + let rule = lock.read(); + let media = rule.stylesheet.media.read(); + let rules = rule.stylesheet.rules.read(); + // FIXME(emilio): Include the nested rules if the stylesheet is + // loaded. + f(&rules.0, Some(&media)) + } CssRule::Namespace(_) | CssRule::Style(_) | CssRule::FontFace(_) | @@ -315,6 +328,7 @@ impl ToCss for CssRule { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { match *self { CssRule::Namespace(ref lock) => lock.read().to_css(dest), + CssRule::Import(ref lock) => lock.read().to_css(dest), CssRule::Style(ref lock) => lock.read().to_css(dest), CssRule::FontFace(ref lock) => lock.read().to_css(dest), CssRule::Viewport(ref lock) => lock.read().to_css(dest), @@ -346,6 +360,34 @@ impl ToCss for NamespaceRule { } } + +/// The [`@import`][import] at-rule. +/// +/// [import]: https://drafts.csswg.org/css-cascade-3/#at-import +#[derive(Debug)] +pub struct ImportRule { + pub url: SpecifiedUrl, + + /// The stylesheet is always present. + /// + /// It contains an empty list of rules and namespace set that is updated + /// when it loads. + pub stylesheet: Arc, +} + +impl ToCss for ImportRule { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + try!(dest.write_str("@import ")); + try!(self.url.to_css(dest)); + let media = self.stylesheet.media.read(); + if !media.is_empty() { + try!(dest.write_str(" ")); + try!(media.to_css(dest)); + } + Ok(()) + } +} + #[derive(Debug)] pub struct KeyframesRule { pub name: Atom, @@ -612,8 +654,32 @@ impl<'a> AtRuleParser for TopLevelRuleParser<'a> { "import" => { if self.state.get() <= State::Imports { self.state.set(State::Imports); - // TODO: support @import - return Err(()) // "@import is not supported yet" + let url = try!(input.expect_url_or_string()); + let url = + try!(SpecifiedUrl::parse_from_string(url, + &self.context)); + + let media = + Arc::new(RwLock::new(parse_media_query_list(input))); + + let is_valid_url = url.url().is_some(); + + let import_rule = Arc::new(RwLock::new( + ImportRule { + url: url, + stylesheet: Arc::new(Stylesheet { + rules: Arc::new(RwLock::new(CssRules(vec![]))), + media: media, + origin: self.context.stylesheet_origin, + base_url: self.context.base_url.clone(), + namespaces: RwLock::new(Namespaces::default()), + dirty_on_viewport_size_change: AtomicBool::new(false), + disabled: AtomicBool::new(false), + }) + } + )); + + return Ok(AtRuleType::WithoutBlock(CssRule::Import(import_rule))) } else { self.state.set(State::Invalid); return Err(()) // "@import must be before any rule but @charset" diff --git a/components/style/stylist.rs b/components/style/stylist.rs index 33f49f08c3e..4cb4a65b081 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -165,6 +165,25 @@ impl Stylist { self.add_stylesheet(stylesheet); } + debug!("Stylist stats:"); + debug!(" - Got {} sibling-affecting selectors", + self.sibling_affecting_selectors.len()); + debug!(" - Got {} non-common-style-attribute-affecting selectors", + self.non_common_style_affecting_attributes_selectors.len()); + debug!(" - Got {} deps for style-hint calculation", + self.state_deps.len()); + + SelectorImpl::each_precomputed_pseudo_element(|pseudo| { + // TODO: Consider not doing this and just getting the rules on the + // fly. It should be a bit slower, but we'd take rid of the + // extra field, and avoid this precomputation entirely. + if let Some(map) = self.pseudos_map.remove(&pseudo) { + let mut declarations = vec![]; + map.user_agent.get_universal_rules(&mut declarations); + self.precomputed_pseudo_element_decls.insert(pseudo, declarations); + } + }); + self.is_device_dirty = false; true } @@ -211,6 +230,10 @@ impl Stylist { } } } + CssRule::Import(ref import) => { + let import = import.read(); + self.add_stylesheet(&import.stylesheet) + } CssRule::Keyframes(ref keyframes_rule) => { let keyframes_rule = keyframes_rule.read(); debug!("Found valid keyframes rule: {:?}", *keyframes_rule); @@ -250,6 +273,7 @@ impl Stylist { }); } + /// Computes the style for a given "precomputed" pseudo-element, taking the /// universal rules and applying them. /// diff --git a/components/style/values/specified/url.rs b/components/style/values/specified/url.rs index d3100586c52..878fbc018b8 100644 --- a/components/style/values/specified/url.rs +++ b/components/style/values/specified/url.rs @@ -11,6 +11,7 @@ use parser::{Parse, ParserContext}; #[cfg(feature = "gecko")] use parser::ParserContextExtraData; use servo_url::ServoUrl; +use std::borrow::Cow; use std::fmt::{self, Write}; use std::ptr; use std::sync::Arc; @@ -76,7 +77,14 @@ pub struct SpecifiedUrl { impl Parse for SpecifiedUrl { fn parse(context: &ParserContext, input: &mut Parser) -> Result { let url = try!(input.expect_url()); + Self::parse_from_string(url, context) + } +} +impl SpecifiedUrl { + pub fn parse_from_string<'a>(url: Cow<'a, str>, + context: &ParserContext) + -> Result { let extra_data = match UrlExtraData::make_from(context) { Some(extra_data) => extra_data, None => { @@ -96,9 +104,7 @@ impl Parse for SpecifiedUrl { extra_data: extra_data, }) } -} -impl SpecifiedUrl { pub fn extra_data(&self) -> &UrlExtraData { &self.extra_data }