mirror of
https://github.com/servo/servo.git
synced 2025-06-24 00:54:32 +01:00
New style sytsem: add selector matching
Also make scribt::dom::element::Element::get_attr ASCII case-insensitive on attribute names, per spec: http://dom.spec.whatwg.org/#dom-element-getattribute
This commit is contained in:
parent
226ccf7e72
commit
20089e4bea
6 changed files with 312 additions and 51 deletions
|
@ -131,7 +131,8 @@ impl<'self> Element {
|
|||
pub fn get_attr(&'self self, name: &str) -> Option<&'self str> {
|
||||
// FIXME: Need an each() that links lifetimes in Rust.
|
||||
for attr in self.attrs.iter() {
|
||||
if eq_slice(attr.name, name) {
|
||||
// FIXME: only case-insensitive in the HTML namespace (as opposed to SVG, etc.)
|
||||
if attr.name.eq_ignore_ascii_case(name) {
|
||||
let val: &str = attr.value;
|
||||
return Some(val);
|
||||
}
|
||||
|
|
|
@ -2,9 +2,15 @@
|
|||
* 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/. */
|
||||
|
||||
// The "real" public API
|
||||
pub use self::selector_matching::{Stylist, StylesheetOrigin};
|
||||
|
||||
|
||||
// Things that need to be public to make the compiler happy
|
||||
pub mod stylesheets;
|
||||
pub mod errors;
|
||||
pub mod selectors;
|
||||
pub mod selector_matching;
|
||||
pub mod properties;
|
||||
pub mod namespaces;
|
||||
pub mod media_queries;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
// This file is a Mako template: http://www.makotemplates.org/
|
||||
|
||||
use std::ascii::StrAsciiExt;
|
||||
use std::at_vec;
|
||||
pub use std::iterator;
|
||||
pub use cssparser::*;
|
||||
pub use style::errors::{ErrorLoggerIterator, log_css_error};
|
||||
|
@ -655,8 +656,8 @@ pub mod shorthands {
|
|||
|
||||
|
||||
pub struct PropertyDeclarationBlock {
|
||||
important: ~[PropertyDeclaration],
|
||||
normal: ~[PropertyDeclaration],
|
||||
important: @[PropertyDeclaration],
|
||||
normal: @[PropertyDeclaration],
|
||||
}
|
||||
|
||||
|
||||
|
@ -668,6 +669,7 @@ pub fn parse_property_declaration_list(input: ~[Node]) -> PropertyDeclarationBlo
|
|||
Decl_AtRule(rule) => log_css_error(
|
||||
rule.location, fmt!("Unsupported at-rule in declaration list: @%s", rule.name)),
|
||||
Declaration(Declaration{ location: l, name: n, value: v, important: i}) => {
|
||||
// TODO: only keep the last valid declaration for a given name.
|
||||
let list = if i { &mut important } else { &mut normal };
|
||||
if !PropertyDeclaration::parse(n, v, list) {
|
||||
log_css_error(l, "Invalid property declaration")
|
||||
|
@ -675,7 +677,11 @@ pub fn parse_property_declaration_list(input: ~[Node]) -> PropertyDeclarationBlo
|
|||
}
|
||||
}
|
||||
}
|
||||
PropertyDeclarationBlock { important: important, normal: normal }
|
||||
PropertyDeclarationBlock {
|
||||
// TODO avoid copying?
|
||||
important: at_vec::to_managed_move(important),
|
||||
normal: at_vec::to_managed_move(normal),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -795,7 +801,8 @@ fn get_initial_values() -> ComputedValues {
|
|||
}
|
||||
|
||||
|
||||
pub fn cascade(applicable_declarations: &[PropertyDeclaration],
|
||||
// Most specific/important declarations last
|
||||
pub fn cascade(applicable_declarations: &[@[PropertyDeclaration]],
|
||||
parent_style: Option< &ComputedValues>)
|
||||
-> ComputedValues {
|
||||
let initial_keep_alive;
|
||||
|
@ -817,14 +824,17 @@ pub fn cascade(applicable_declarations: &[PropertyDeclaration],
|
|||
"Inherit" if property.is_inherited else "Initial"}),
|
||||
% endfor
|
||||
};
|
||||
for declaration in applicable_declarations.iter() {
|
||||
match declaration {
|
||||
% for property in LONGHANDS:
|
||||
&${property.ident}_declaration(ref value) => {
|
||||
// Overwrite earlier declarations.
|
||||
specified.${property.ident} = (*value).clone() // TODO: can we avoid a copy?
|
||||
}
|
||||
% endfor
|
||||
for sub_list in applicable_declarations.iter() {
|
||||
for declaration in sub_list.iter() {
|
||||
match declaration {
|
||||
% for property in LONGHANDS:
|
||||
&${property.ident}_declaration(ref value) => {
|
||||
// Overwrite earlier declarations.
|
||||
// TODO: can we avoid a copy?
|
||||
specified.${property.ident} = (*value).clone()
|
||||
}
|
||||
% endfor
|
||||
}
|
||||
}
|
||||
}
|
||||
// This assumes that the computed and specified values have the same Rust type.
|
||||
|
|
243
src/components/script/style/selector_matching.rs
Normal file
243
src/components/script/style/selector_matching.rs
Normal file
|
@ -0,0 +1,243 @@
|
|||
/* 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/. */
|
||||
|
||||
use std::ascii::StrAsciiExt;
|
||||
use extra::sort::tim_sort;
|
||||
|
||||
use style::selectors::*;
|
||||
use style::stylesheets::parse_stylesheet;
|
||||
use style::media_queries::{Device, Screen};
|
||||
use style::properties::{ComputedValues, cascade, PropertyDeclaration};
|
||||
use dom::node::{AbstractNode, ScriptView};
|
||||
use dom::element::Element;
|
||||
|
||||
|
||||
pub enum StylesheetOrigin {
|
||||
UserAgentOrigin,
|
||||
AuthorOrigin,
|
||||
UserOrigin,
|
||||
}
|
||||
|
||||
|
||||
pub struct Stylist {
|
||||
priv ua_rules: PerOriginRules,
|
||||
priv author_rules: PerOriginRules,
|
||||
priv user_rules: PerOriginRules,
|
||||
}
|
||||
|
||||
|
||||
impl Stylist {
|
||||
#[inline]
|
||||
pub fn new() -> Stylist {
|
||||
Stylist {
|
||||
ua_rules: PerOriginRules::new(),
|
||||
author_rules: PerOriginRules::new(),
|
||||
user_rules: PerOriginRules::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_stylesheet(&mut self, css_source: &str, origin: StylesheetOrigin) {
|
||||
let stylesheet = parse_stylesheet(css_source);
|
||||
let rules = match origin {
|
||||
UserAgentOrigin => &mut self.ua_rules,
|
||||
AuthorOrigin => &mut self.author_rules,
|
||||
UserOrigin => &mut self.user_rules,
|
||||
};
|
||||
let mut has_normal_declarations = false;
|
||||
let mut has_important_declarations = false;
|
||||
|
||||
macro_rules! append(
|
||||
($priority: ident, $flag: ident) => {
|
||||
if style_rule.declarations.$priority.len() > 0 {
|
||||
$flag = true;
|
||||
for selector in style_rule.selectors.iter() {
|
||||
rules.$priority.push(Rule {
|
||||
selector: *selector,
|
||||
declarations: style_rule.declarations.$priority,
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
)
|
||||
|
||||
let device = &Device { media_type: Screen }; // TODO, use Print when printing
|
||||
for style_rule in stylesheet.iter_style_rules(device) {
|
||||
append!(normal, has_normal_declarations);
|
||||
append!(important, has_important_declarations);
|
||||
}
|
||||
|
||||
if has_normal_declarations {
|
||||
tim_sort(rules.normal)
|
||||
}
|
||||
if has_important_declarations {
|
||||
tim_sort(rules.important)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_computed_style(&self, element: AbstractNode<ScriptView>,
|
||||
parent_style: Option<&ComputedValues>,
|
||||
pseudo_element: Option<PseudoElement>)
|
||||
-> ComputedValues {
|
||||
assert!(element.is_element())
|
||||
// Only the root does not inherit.
|
||||
// The root has no parent or a non-element parent.
|
||||
assert_eq!(
|
||||
parent_style.is_none(),
|
||||
match element.parent_node() {
|
||||
None => true,
|
||||
Some(ref node) => !node.is_element()
|
||||
}
|
||||
);
|
||||
let mut applicable_declarations = ~[]; // TODO: use an iterator?
|
||||
|
||||
macro_rules! append(
|
||||
($rules: expr) => {
|
||||
for rule in $rules.iter() {
|
||||
if matches_selector(rule.selector, element, pseudo_element) {
|
||||
applicable_declarations.push(rule.declarations)
|
||||
}
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
// In cascading order
|
||||
append!(self.ua_rules.normal);
|
||||
append!(self.user_rules.normal);
|
||||
append!(self.author_rules.normal);
|
||||
// TODO add style attribute
|
||||
append!(self.author_rules.important);
|
||||
append!(self.user_rules.important);
|
||||
append!(self.ua_rules.important);
|
||||
|
||||
cascade(applicable_declarations, parent_style)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct PerOriginRules {
|
||||
normal: ~[Rule],
|
||||
important: ~[Rule],
|
||||
}
|
||||
|
||||
impl PerOriginRules {
|
||||
#[inline]
|
||||
fn new() -> PerOriginRules {
|
||||
PerOriginRules { normal: ~[], important: ~[] }
|
||||
}
|
||||
}
|
||||
|
||||
#[deriving(Clone)]
|
||||
struct Rule {
|
||||
selector: @Selector,
|
||||
declarations: @[PropertyDeclaration],
|
||||
}
|
||||
|
||||
|
||||
impl Ord for Rule {
|
||||
#[inline]
|
||||
fn lt(&self, other: &Rule) -> bool {
|
||||
self.selector.specificity < other.selector.specificity
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[inline]
|
||||
fn matches_selector(selector: &Selector, element: AbstractNode<ScriptView>,
|
||||
pseudo_element: Option<PseudoElement>) -> bool {
|
||||
selector.pseudo_element == pseudo_element &&
|
||||
matches_compound_selector(&selector.compound_selectors, element)
|
||||
}
|
||||
|
||||
|
||||
fn matches_compound_selector(selector: &CompoundSelector,
|
||||
element: AbstractNode<ScriptView>) -> bool {
|
||||
if do element.with_imm_element |element| {
|
||||
!do selector.simple_selectors.iter().all |simple_selector| {
|
||||
matches_simple_selector(simple_selector, element)
|
||||
}
|
||||
} {
|
||||
return false
|
||||
}
|
||||
match selector.next {
|
||||
None => true,
|
||||
Some((ref next_selector, combinator)) => {
|
||||
let (siblings, just_one) = match combinator {
|
||||
Child => (false, true),
|
||||
Descendant => (false, false),
|
||||
NextSibling => (true, true),
|
||||
LaterSibling => (true, false),
|
||||
};
|
||||
let mut node = element;
|
||||
loop {
|
||||
match if siblings { node.prev_sibling() } else { node.parent_node() } {
|
||||
None => return false,
|
||||
Some(next_node) => node = next_node,
|
||||
}
|
||||
if node.is_element() {
|
||||
if matches_compound_selector(&**next_selector, node) {
|
||||
return true
|
||||
} else if just_one {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn matches_simple_selector(selector: &SimpleSelector, element: &Element) -> bool {
|
||||
static WHITESPACE: &'static [char] = &'static [' ', '\t', '\n', '\r', '\x0C'];
|
||||
|
||||
match *selector {
|
||||
// TODO: case-sensitivity depends on the document type
|
||||
LocalNameSelector(ref name) => element.tag_name.eq_ignore_ascii_case(name.as_slice()),
|
||||
NamespaceSelector(_) => false, // TODO, when the DOM supports namespaces on elements.
|
||||
// TODO: case-sensitivity depends on the document type and quirks mode
|
||||
// TODO: cache and intern IDs on elements.
|
||||
IDSelector(ref id) => element.get_attr("id") == Some(id.as_slice()),
|
||||
// TODO: cache and intern classe names on elements.
|
||||
ClassSelector(ref class) => match element.get_attr("class") {
|
||||
None => false,
|
||||
// TODO: case-sensitivity depends on the document type and quirks mode
|
||||
Some(ref class_attr)
|
||||
=> class_attr.split_iter(WHITESPACE).any(|c| c == class.as_slice()),
|
||||
},
|
||||
|
||||
AttrExists(ref attr) => match_attribute(attr, element, |_| true),
|
||||
AttrEqual(ref attr, ref value) => match_attribute(attr, element, |v| v == value.as_slice()),
|
||||
AttrIncludes(ref attr, ref value) => do match_attribute(attr, element) |attr_value| {
|
||||
attr_value.split_iter(WHITESPACE).any(|v| v == value.as_slice())
|
||||
},
|
||||
AttrDashMatch(ref attr, ref value, ref dashing_value)
|
||||
=> do match_attribute(attr, element) |attr_value| {
|
||||
attr_value == value.as_slice() || attr_value.starts_with(dashing_value.as_slice())
|
||||
},
|
||||
AttrPrefixMatch(ref attr, ref value) => do match_attribute(attr, element) |attr_value| {
|
||||
attr_value.starts_with(value.as_slice())
|
||||
},
|
||||
AttrSubstringMatch(ref attr, ref value) => do match_attribute(attr, element) |attr_value| {
|
||||
attr_value.contains(value.as_slice())
|
||||
},
|
||||
AttrSuffixMatch(ref attr, ref value) => do match_attribute(attr, element) |attr_value| {
|
||||
attr_value.ends_with(value.as_slice())
|
||||
},
|
||||
|
||||
Negation(ref negated) => {
|
||||
!negated.iter().all(|s| matches_simple_selector(s, element))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[inline]
|
||||
fn match_attribute(attr: &AttrSelector, element: &Element, f: &fn(&str)-> bool) -> bool {
|
||||
match attr.namespace {
|
||||
Some(_) => false, // TODO, when the DOM supports namespaces on attributes
|
||||
None => match element.get_attr(attr.name) {
|
||||
None => false,
|
||||
Some(ref value) => f(value.as_slice())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@ pub struct Selector {
|
|||
pub static STYLE_ATTRIBUTE_SPECIFICITY: u32 = 1 << 31;
|
||||
|
||||
|
||||
#[deriving(Eq)]
|
||||
pub enum PseudoElement {
|
||||
Before,
|
||||
After,
|
||||
|
@ -40,30 +41,29 @@ pub enum Combinator {
|
|||
pub enum SimpleSelector {
|
||||
IDSelector(~str),
|
||||
ClassSelector(~str),
|
||||
LocalNameSelector{lowercase_name: ~str, cased_name: ~str},
|
||||
LocalNameSelector(~str),
|
||||
NamespaceSelector(~str),
|
||||
|
||||
// Attribute selectors
|
||||
AttrExists(AttrSelector), // [foo]
|
||||
AttrEqual(AttrSelector, ~str), // [foo=bar]
|
||||
AttrIncludes(AttrSelector, ~str), // [foo~=bar]
|
||||
AttrDashMatch(AttrSelector, ~str), // [foo|=bar]
|
||||
AttrDashMatch(AttrSelector, ~str, ~str), // [foo|=bar] Second string is the first + "-"
|
||||
AttrPrefixMatch(AttrSelector, ~str), // [foo^=bar]
|
||||
AttrSubstringMatch(AttrSelector, ~str), // [foo*=bar]
|
||||
AttrSuffixMatch(AttrSelector, ~str), // [foo$=bar]
|
||||
|
||||
// Pseudo-classes
|
||||
Empty,
|
||||
Root,
|
||||
Lang(~str),
|
||||
NthChild(i32, i32),
|
||||
// Empty,
|
||||
// Root,
|
||||
// Lang(~str),
|
||||
// NthChild(i32, i32),
|
||||
Negation(~[SimpleSelector]),
|
||||
// ...
|
||||
}
|
||||
|
||||
pub struct AttrSelector {
|
||||
lowercase_name: ~str,
|
||||
cased_name: ~str,
|
||||
name: ~str,
|
||||
namespace: Option<~str>,
|
||||
}
|
||||
|
||||
|
@ -73,7 +73,7 @@ type Iter = iterator::Peekable<ComponentValue, vec::MoveIterator<ComponentValue>
|
|||
|
||||
// None means invalid selector
|
||||
pub fn parse_selector_list(input: ~[ComponentValue], namespaces: &NamespaceMap)
|
||||
-> Option<~[Selector]> {
|
||||
-> Option<~[@Selector]> {
|
||||
let iter = &mut input.move_iter().peekable();
|
||||
let first = match parse_selector(iter, namespaces) {
|
||||
None => return None,
|
||||
|
@ -99,7 +99,7 @@ pub fn parse_selector_list(input: ~[ComponentValue], namespaces: &NamespaceMap)
|
|||
|
||||
// None means invalid selector
|
||||
fn parse_selector(iter: &mut Iter, namespaces: &NamespaceMap)
|
||||
-> Option<Selector> {
|
||||
-> Option<@Selector> {
|
||||
let (first, pseudo_element) = match parse_simple_selectors(iter, namespaces) {
|
||||
None => return None,
|
||||
Some(result) => result
|
||||
|
@ -130,12 +130,11 @@ fn parse_selector(iter: &mut Iter, namespaces: &NamespaceMap)
|
|||
}
|
||||
}
|
||||
}
|
||||
let selector = Selector{
|
||||
Some(@Selector {
|
||||
specificity: compute_specificity(&compound, &pseudo_element),
|
||||
compound_selectors: compound,
|
||||
pseudo_element: pseudo_element,
|
||||
};
|
||||
Some(selector)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
@ -168,12 +167,12 @@ fn compute_specificity(mut selector: &CompoundSelector,
|
|||
specificity: &mut Specificity) {
|
||||
for simple_selector in simple_selectors.iter() {
|
||||
match simple_selector {
|
||||
&LocalNameSelector{_} => specificity.element_selectors += 1,
|
||||
&LocalNameSelector(*) => specificity.element_selectors += 1,
|
||||
&IDSelector(*) => specificity.id_selectors += 1,
|
||||
&ClassSelector(*)
|
||||
| &AttrExists(*) | &AttrEqual(*) | &AttrIncludes(*) | &AttrDashMatch(*)
|
||||
| &AttrPrefixMatch(*) | &AttrSubstringMatch(*) | &AttrSuffixMatch(*)
|
||||
| &Empty | &Root | &Lang(*) | &NthChild(*)
|
||||
// | &Empty | &Root | &Lang(*) | &NthChild(*)
|
||||
=> specificity.class_like_selectors += 1,
|
||||
&NamespaceSelector(*) => (),
|
||||
&Negation(ref negated)
|
||||
|
@ -229,10 +228,7 @@ fn parse_type_selector(iter: &mut Iter, namespaces: &NamespaceMap)
|
|||
None => (),
|
||||
}
|
||||
match local_name {
|
||||
Some(name) => simple_selectors.push(LocalNameSelector{
|
||||
lowercase_name: name.to_ascii_lower(),
|
||||
cased_name: name,
|
||||
}),
|
||||
Some(name) => simple_selectors.push(LocalNameSelector(name)),
|
||||
None => (),
|
||||
}
|
||||
Some(Some(simple_selectors))
|
||||
|
@ -369,11 +365,11 @@ fn parse_attribute_selector(content: ~[ComponentValue], namespaces: &NamespaceMa
|
|||
Some(Some((_, None))) => fail!("Implementation error, this should not happen."),
|
||||
Some(Some((namespace, Some(local_name)))) => AttrSelector {
|
||||
namespace: namespace,
|
||||
lowercase_name: local_name.to_ascii_lower(),
|
||||
cased_name: local_name,
|
||||
name: local_name,
|
||||
},
|
||||
};
|
||||
skip_whitespace(iter);
|
||||
// TODO: deal with empty value or value containing whitespace (see spec)
|
||||
macro_rules! get_value( () => {{
|
||||
skip_whitespace(iter);
|
||||
match iter.next() {
|
||||
|
@ -385,7 +381,11 @@ fn parse_attribute_selector(content: ~[ComponentValue], namespaces: &NamespaceMa
|
|||
None => AttrExists(attr), // [foo]
|
||||
Some(Delim('=')) => AttrEqual(attr, get_value!()), // [foo=bar]
|
||||
Some(IncludeMatch) => AttrIncludes(attr, get_value!()), // [foo~=bar]
|
||||
Some(DashMatch) => AttrDashMatch(attr, get_value!()), // [foo|=bar]
|
||||
Some(DashMatch) => {
|
||||
let value = get_value!();
|
||||
let dashing_value = value + "-";
|
||||
AttrDashMatch(attr, value, dashing_value) // [foo|=bar]
|
||||
},
|
||||
Some(PrefixMatch) => AttrPrefixMatch(attr, get_value!()), // [foo^=bar]
|
||||
Some(SubstringMatch) => AttrSubstringMatch(attr, get_value!()), // [foo*=bar]
|
||||
Some(SuffixMatch) => AttrSuffixMatch(attr, get_value!()), // [foo$=bar]
|
||||
|
@ -398,8 +398,8 @@ fn parse_attribute_selector(content: ~[ComponentValue], namespaces: &NamespaceMa
|
|||
|
||||
fn parse_simple_pseudo_class(name: ~str) -> Option<Either<SimpleSelector, PseudoElement>> {
|
||||
match name.to_ascii_lower().as_slice() {
|
||||
"root" => Some(Left(Root)),
|
||||
"empty" => Some(Left(Empty)),
|
||||
// "root" => Some(Left(Root)),
|
||||
// "empty" => Some(Left(Empty)),
|
||||
|
||||
// Supported CSS 2.1 pseudo-elements only.
|
||||
"before" => Some(Right(Before)),
|
||||
|
@ -415,8 +415,8 @@ fn parse_functional_pseudo_class(name: ~str, arguments: ~[ComponentValue],
|
|||
namespaces: &NamespaceMap, inside_negation: bool)
|
||||
-> Option<SimpleSelector> {
|
||||
match name.to_ascii_lower().as_slice() {
|
||||
"lang" => parse_lang(arguments),
|
||||
"nth-child" => parse_nth(arguments).map(|&(a, b)| NthChild(a, b)),
|
||||
// "lang" => parse_lang(arguments),
|
||||
// "nth-child" => parse_nth(arguments).map(|&(a, b)| NthChild(a, b)),
|
||||
"not" => if inside_negation { None } else { parse_negation(arguments, namespaces) },
|
||||
_ => None
|
||||
}
|
||||
|
@ -435,16 +435,16 @@ fn parse_pseudo_element(name: ~str) -> Option<PseudoElement> {
|
|||
}
|
||||
|
||||
|
||||
fn parse_lang(arguments: ~[ComponentValue]) -> Option<SimpleSelector> {
|
||||
let mut iter = arguments.move_skip_whitespace();
|
||||
match iter.next() {
|
||||
Some(Ident(value)) => {
|
||||
if "" == value || iter.next().is_some() { None }
|
||||
else { Some(Lang(value)) }
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
//fn parse_lang(arguments: ~[ComponentValue]) -> Option<SimpleSelector> {
|
||||
// let mut iter = arguments.move_skip_whitespace();
|
||||
// match iter.next() {
|
||||
// Some(Ident(value)) => {
|
||||
// if "" == value || iter.next().is_some() { None }
|
||||
// else { Some(Lang(value)) }
|
||||
// },
|
||||
// _ => None,
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
// Level 3: Parse ONE simple_selector
|
||||
|
|
|
@ -26,7 +26,7 @@ pub enum CSSRule {
|
|||
|
||||
|
||||
pub struct StyleRule {
|
||||
selectors: ~[selectors::Selector],
|
||||
selectors: ~[@selectors::Selector],
|
||||
declarations: properties::PropertyDeclarationBlock,
|
||||
}
|
||||
|
||||
|
@ -117,7 +117,8 @@ pub fn parse_nested_at_rule(lower_name: &str, rule: AtRule,
|
|||
|
||||
|
||||
impl Stylesheet {
|
||||
fn iter_style_rules<'a>(&'a self, device: &'a media_queries::Device) -> StyleRuleIterator<'a> {
|
||||
pub fn iter_style_rules<'a>(&'a self, device: &'a media_queries::Device)
|
||||
-> StyleRuleIterator<'a> {
|
||||
StyleRuleIterator { device: device, stack: ~[(self.rules.as_slice(), 0)] }
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue