style: Add a struct to represent import rules, and parse them correctly.

This commit is contained in:
Emilio Cobos Álvarez 2016-12-16 12:05:19 +01:00
parent e5d783c454
commit 444fef164e
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
3 changed files with 103 additions and 7 deletions

View file

@ -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<RwLock<NamespaceRule>>),
Import(Arc<RwLock<ImportRule>>),
Style(Arc<RwLock<StyleRule>>),
Media(Arc<RwLock<MediaRule>>),
FontFace(Arc<RwLock<FontFaceRule>>),
@ -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<F, R>(&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<W>(&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<Stylesheet>,
}
impl ToCss for ImportRule {
fn to_css<W>(&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"

View file

@ -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.
///

View file

@ -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<Self, ()> {
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<Self, ()> {
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
}