mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
style: Address review comments relating to bgcolor
and column spans
This commit is contained in:
parent
17835ba0cb
commit
a1ea44b294
33 changed files with 712 additions and 422 deletions
|
@ -8,11 +8,10 @@
|
|||
use node::{TElement, TElementAttributes, TNode};
|
||||
use properties::{BackgroundColorDeclaration, BorderBottomWidthDeclaration};
|
||||
use properties::{BorderLeftWidthDeclaration, BorderRightWidthDeclaration};
|
||||
use properties::{BorderTopWidthDeclaration, ServoColumnSpanDeclaration, SpecifiedValue};
|
||||
use properties::{WidthDeclaration, specified};
|
||||
use properties::{BorderTopWidthDeclaration, SpecifiedValue, WidthDeclaration, specified};
|
||||
use selector_matching::{DeclarationBlock, Stylist};
|
||||
|
||||
use cssparser::{RGBA, RGBAColor};
|
||||
use cssparser::RGBAColor;
|
||||
use servo_util::geometry::Au;
|
||||
use servo_util::smallvec::VecLike;
|
||||
use servo_util::str::{AutoLpa, LengthLpa, PercentageLpa};
|
||||
|
@ -114,14 +113,6 @@ impl PresentationalHintSynthesis for Stylist {
|
|||
*shareable = false
|
||||
}
|
||||
}
|
||||
match element.get_unsigned_integer_attribute(ColSpanUnsignedIntegerAttribute) {
|
||||
None => {}
|
||||
Some(value) => {
|
||||
matching_rules_list.vec_push(DeclarationBlock::from_declaration(
|
||||
ServoColumnSpanDeclaration(SpecifiedValue(value))));
|
||||
*shareable = false
|
||||
}
|
||||
}
|
||||
self.synthesize_presentational_hint_for_legacy_background_color_attribute(
|
||||
element,
|
||||
matching_rules_list,
|
||||
|
@ -141,7 +132,8 @@ impl PresentationalHintSynthesis for Stylist {
|
|||
matching_rules_list,
|
||||
shareable);
|
||||
}
|
||||
name if *name == atom!("body") => {
|
||||
name if *name == atom!("body") || *name == atom!("tr") || *name == atom!("thead") ||
|
||||
*name == atom!("tbody") || *name == atom!("tfoot") => {
|
||||
self.synthesize_presentational_hint_for_legacy_background_color_attribute(
|
||||
element,
|
||||
matching_rules_list,
|
||||
|
@ -187,12 +179,7 @@ impl PresentationalHintSynthesis for Stylist {
|
|||
None => {}
|
||||
Some(color) => {
|
||||
matching_rules_list.vec_push(DeclarationBlock::from_declaration(
|
||||
BackgroundColorDeclaration(SpecifiedValue(RGBAColor(RGBA {
|
||||
red: color.red as f32 / 255.0,
|
||||
green: color.green as f32 / 255.0,
|
||||
blue: color.blue as f32 / 255.0,
|
||||
alpha: 1.0,
|
||||
})))));
|
||||
BackgroundColorDeclaration(SpecifiedValue(RGBAColor(color)))));
|
||||
*shareable = false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,9 +50,9 @@ pub use properties::{CSSFloat, DeclaredValue, PropertyDeclarationParseResult};
|
|||
pub use properties::{Angle, AngleOrCorner, AngleAoc, CornerAoc};
|
||||
pub use properties::{Left, Right, Bottom, Top};
|
||||
pub use node::{TElement, TElementAttributes, TNode};
|
||||
pub use selectors::{PseudoElement, Before, After, SelectorList, parse_selector_list_from_str};
|
||||
pub use selectors::{PseudoElement, Before, After, ParserContext, SelectorList};
|
||||
pub use selectors::{AttrSelector, NamespaceConstraint, SpecificNamespace, AnyNamespace};
|
||||
pub use selectors::{SimpleSelector, LocalNameSelector};
|
||||
pub use selectors::{SimpleSelector, LocalNameSelector, parse_selector_list_from_str};
|
||||
pub use cssparser::{Color, RGBA};
|
||||
pub use legacy::{BgColorSimpleColorAttribute, BorderUnsignedIntegerAttribute};
|
||||
pub use legacy::{ColSpanUnsignedIntegerAttribute, IntegerAttribute, LengthAttribute};
|
||||
|
|
|
@ -8,12 +8,14 @@ use cssparser::ast::*;
|
|||
|
||||
use errors::{ErrorLoggerIterator, log_css_error};
|
||||
use geom::size::TypedSize2D;
|
||||
use stylesheets::{CSSRule, CSSMediaRule, parse_style_rule, parse_nested_at_rule};
|
||||
use selectors::ParserContext;
|
||||
use stylesheets::{CSSRule, CSSMediaRule};
|
||||
use namespaces::NamespaceMap;
|
||||
use parsing_utils::{BufferedIter, ParserIter};
|
||||
use properties::common_types::*;
|
||||
use properties::longhands;
|
||||
use servo_util::geometry::ViewportPx;
|
||||
use stylesheets;
|
||||
use url::Url;
|
||||
|
||||
pub struct MediaRule {
|
||||
|
@ -95,8 +97,11 @@ impl Device {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn parse_media_rule(rule: AtRule, parent_rules: &mut Vec<CSSRule>,
|
||||
namespaces: &NamespaceMap, base_url: &Url) {
|
||||
pub fn parse_media_rule(context: &ParserContext,
|
||||
rule: AtRule,
|
||||
parent_rules: &mut Vec<CSSRule>,
|
||||
namespaces: &NamespaceMap,
|
||||
base_url: &Url) {
|
||||
let media_queries = parse_media_query_list(rule.prelude.as_slice());
|
||||
let block = match rule.block {
|
||||
Some(block) => block,
|
||||
|
@ -108,9 +113,17 @@ pub fn parse_media_rule(rule: AtRule, parent_rules: &mut Vec<CSSRule>,
|
|||
let mut rules = vec!();
|
||||
for rule in ErrorLoggerIterator(parse_rule_list(block.into_iter())) {
|
||||
match rule {
|
||||
QualifiedRule_(rule) => parse_style_rule(rule, &mut rules, namespaces, base_url),
|
||||
AtRule_(rule) => parse_nested_at_rule(
|
||||
rule.name.as_slice().to_ascii_lower().as_slice(), rule, &mut rules, namespaces, base_url),
|
||||
QualifiedRule_(rule) => {
|
||||
stylesheets::parse_style_rule(context, rule, &mut rules, namespaces, base_url)
|
||||
}
|
||||
AtRule_(rule) => {
|
||||
stylesheets::parse_nested_at_rule(context,
|
||||
rule.name.as_slice().to_ascii_lower().as_slice(),
|
||||
rule,
|
||||
&mut rules,
|
||||
namespaces,
|
||||
base_url)
|
||||
}
|
||||
}
|
||||
}
|
||||
parent_rules.push(CSSMediaRule(MediaRule {
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
//! Traits that nodes must implement. Breaks the otherwise-cyclic dependency between layout and
|
||||
//! style.
|
||||
|
||||
use cssparser::RGBA;
|
||||
use legacy::{IntegerAttribute, LengthAttribute, SimpleColorAttribute, UnsignedIntegerAttribute};
|
||||
use selectors::AttrSelector;
|
||||
use servo_util::str::{LengthOrPercentageOrAuto, SimpleColor};
|
||||
use servo_util::str::LengthOrPercentageOrAuto;
|
||||
use string_cache::{Atom, Namespace};
|
||||
|
||||
pub trait TNode<'a, E: TElement<'a>> : Clone + Copy {
|
||||
|
@ -60,5 +61,5 @@ pub trait TElementAttributes : Copy {
|
|||
fn get_length_attribute(self, attribute: LengthAttribute) -> LengthOrPercentageOrAuto;
|
||||
fn get_integer_attribute(self, attribute: IntegerAttribute) -> Option<i32>;
|
||||
fn get_unsigned_integer_attribute(self, attribute: UnsignedIntegerAttribute) -> Option<u32>;
|
||||
fn get_simple_color_attribute(self, attribute: SimpleColorAttribute) -> Option<SimpleColor>;
|
||||
fn get_simple_color_attribute(self, attribute: SimpleColorAttribute) -> Option<RGBA>;
|
||||
}
|
||||
|
|
|
@ -1316,34 +1316,6 @@ pub mod longhands {
|
|||
|
||||
${single_keyword("table-layout", "auto fixed")}
|
||||
|
||||
<%self:single_component_value name="-servo-column-span">
|
||||
// The handling of this property is not well-specified by INTRINSIC, but its presence is
|
||||
// assumed. HTML5 14.3.9 specifies that the `colspan` attribute is to be a nonnegative
|
||||
// integer.
|
||||
pub use super::computed_as_specified as to_computed_value;
|
||||
pub mod computed_value {
|
||||
pub type T = u32;
|
||||
}
|
||||
pub type SpecifiedValue = computed_value::T;
|
||||
|
||||
#[inline]
|
||||
pub fn get_initial_value() -> computed_value::T {
|
||||
1
|
||||
}
|
||||
|
||||
pub fn from_component_value(input: &ComponentValue, _: &Url) -> Result<SpecifiedValue,()> {
|
||||
match input {
|
||||
&Number(ref value) => {
|
||||
match value.int_value {
|
||||
None => Err(()),
|
||||
Some(n) => Ok(n as SpecifiedValue),
|
||||
}
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
</%self:single_component_value>
|
||||
|
||||
// CSS 2.1, Section 18 - User interface
|
||||
|
||||
|
||||
|
|
|
@ -20,9 +20,22 @@ use legacy::PresentationalHintSynthesis;
|
|||
use media_queries::Device;
|
||||
use node::{TElement, TElementAttributes, TNode};
|
||||
use properties::{PropertyDeclaration, PropertyDeclarationBlock};
|
||||
use selectors::*;
|
||||
use selectors::{After, AnyLink, AttrDashMatch, AttrEqual};
|
||||
use selectors::{AttrExists, AttrIncludes, AttrPrefixMatch};
|
||||
use selectors::{AttrSubstringMatch, AttrSuffixMatch, Before, CaseInsensitive, CaseSensitive};
|
||||
use selectors::{Checked, Child, ClassSelector};
|
||||
use selectors::{CompoundSelector, Descendant, Disabled, Enabled, FirstChild, FirstOfType};
|
||||
use selectors::{Hover, IDSelector, LastChild, LastOfType};
|
||||
use selectors::{LaterSibling, LocalName, LocalNameSelector};
|
||||
use selectors::{NamespaceSelector, Link, Negation};
|
||||
use selectors::{NextSibling, NthChild};
|
||||
use selectors::{NthLastChild, NthLastOfType};
|
||||
use selectors::{NthOfType, OnlyChild, OnlyOfType, PseudoElement, Root};
|
||||
use selectors::{SelectorList, ServoNonzeroBorder, SimpleSelector, Visited};
|
||||
use selectors::{get_selector_list_selectors};
|
||||
use stylesheets::{Stylesheet, iter_stylesheet_media_rules, iter_stylesheet_style_rules};
|
||||
|
||||
#[deriving(Clone, PartialEq)]
|
||||
pub enum StylesheetOrigin {
|
||||
UserAgentOrigin,
|
||||
AuthorOrigin,
|
||||
|
@ -1166,12 +1179,16 @@ mod tests {
|
|||
/// Each sublist of the result contains the Rules for one StyleRule.
|
||||
fn get_mock_rules(css_selectors: &[&str]) -> Vec<Vec<Rule>> {
|
||||
use namespaces::NamespaceMap;
|
||||
use selectors::parse_selector_list;
|
||||
use selectors::{ParserContext, parse_selector_list};
|
||||
use selector_matching::AuthorOrigin;
|
||||
use cssparser::tokenize;
|
||||
|
||||
let namespaces = NamespaceMap::new();
|
||||
css_selectors.iter().enumerate().map(|(i, selectors)| {
|
||||
parse_selector_list(tokenize(*selectors).map(|(c, _)| c), &namespaces)
|
||||
let context = ParserContext {
|
||||
origin: AuthorOrigin,
|
||||
};
|
||||
parse_selector_list(&context, tokenize(*selectors).map(|(c, _)| c), &namespaces)
|
||||
.unwrap().into_iter().map(|s| {
|
||||
Rule {
|
||||
selector: s.compound_selectors.clone(),
|
||||
|
|
|
@ -9,10 +9,16 @@ use sync::Arc;
|
|||
use cssparser::ast::*;
|
||||
use cssparser::{tokenize, parse_nth};
|
||||
|
||||
use selector_matching::{StylesheetOrigin, UserAgentOrigin};
|
||||
use string_cache::{Atom, Namespace};
|
||||
|
||||
use namespaces::NamespaceMap;
|
||||
|
||||
/// Ambient data used by the parser.
|
||||
pub struct ParserContext {
|
||||
/// The origin of this stylesheet.
|
||||
pub origin: StylesheetOrigin,
|
||||
}
|
||||
|
||||
#[deriving(PartialEq, Clone)]
|
||||
pub struct Selector {
|
||||
|
@ -112,12 +118,6 @@ pub enum NamespaceConstraint {
|
|||
}
|
||||
|
||||
|
||||
pub fn parse_selector_list_from_str(input: &str) -> Result<SelectorList, ()> {
|
||||
let namespaces = NamespaceMap::new();
|
||||
let iter = tokenize(input).map(|(token, _)| token);
|
||||
parse_selector_list(iter, &namespaces).map(|s| SelectorList { selectors: s })
|
||||
}
|
||||
|
||||
/// Re-exported to script, but opaque.
|
||||
pub struct SelectorList {
|
||||
selectors: Vec<Selector>
|
||||
|
@ -128,70 +128,9 @@ pub fn get_selector_list_selectors<'a>(selector_list: &'a SelectorList) -> &'a [
|
|||
selector_list.selectors.as_slice()
|
||||
}
|
||||
|
||||
/// Parse a comma-separated list of Selectors.
|
||||
/// aka Selector Group in http://www.w3.org/TR/css3-selectors/#grouping
|
||||
///
|
||||
/// Return the Selectors or None if there is an invalid selector.
|
||||
pub fn parse_selector_list<I: Iterator<ComponentValue>>(
|
||||
iter: I, namespaces: &NamespaceMap)
|
||||
-> Result<Vec<Selector>, ()> {
|
||||
let iter = &mut iter.peekable();
|
||||
let mut results = vec![try!(parse_selector(iter, namespaces))];
|
||||
|
||||
loop {
|
||||
skip_whitespace(iter);
|
||||
match iter.peek() {
|
||||
None => break, // EOF
|
||||
Some(&Comma) => {
|
||||
iter.next();
|
||||
}
|
||||
_ => return Err(()),
|
||||
}
|
||||
results.push(try!(parse_selector(iter, namespaces)));
|
||||
}
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
|
||||
type Iter<I> = iter::Peekable<ComponentValue, I>;
|
||||
|
||||
/// Build up a Selector.
|
||||
/// selector : simple_selector_sequence [ combinator simple_selector_sequence ]* ;
|
||||
///
|
||||
/// `Err` means invalid selector.
|
||||
fn parse_selector<I: Iterator<ComponentValue>>(
|
||||
iter: &mut Iter<I>, namespaces: &NamespaceMap)
|
||||
-> Result<Selector, ()> {
|
||||
let (first, mut pseudo_element) = try!(parse_simple_selectors(iter, namespaces));
|
||||
let mut compound = CompoundSelector{ simple_selectors: first, next: None };
|
||||
|
||||
while pseudo_element.is_none() {
|
||||
let any_whitespace = skip_whitespace(iter);
|
||||
let combinator = match iter.peek() {
|
||||
None => break, // EOF
|
||||
Some(&Comma) => break,
|
||||
Some(&Delim('>')) => { iter.next(); Child },
|
||||
Some(&Delim('+')) => { iter.next(); NextSibling },
|
||||
Some(&Delim('~')) => { iter.next(); LaterSibling },
|
||||
Some(_) => {
|
||||
if any_whitespace { Descendant }
|
||||
else { return Err(()) }
|
||||
}
|
||||
};
|
||||
let (simple_selectors, pseudo) = try!(parse_simple_selectors(iter, namespaces));
|
||||
compound = CompoundSelector {
|
||||
simple_selectors: simple_selectors,
|
||||
next: Some((box compound, combinator))
|
||||
};
|
||||
pseudo_element = pseudo;
|
||||
}
|
||||
Ok(Selector {
|
||||
specificity: compute_specificity(&compound, &pseudo_element),
|
||||
compound_selectors: Arc::new(compound),
|
||||
pseudo_element: pseudo_element,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
fn compute_specificity(mut selector: &CompoundSelector,
|
||||
pseudo_element: &Option<PseudoElement>) -> u32 {
|
||||
|
@ -248,32 +187,6 @@ fn compute_specificity(mut selector: &CompoundSelector,
|
|||
}
|
||||
|
||||
|
||||
/// simple_selector_sequence
|
||||
/// : [ type_selector | universal ] [ HASH | class | attrib | pseudo | negation ]*
|
||||
/// | [ HASH | class | attrib | pseudo | negation ]+
|
||||
///
|
||||
/// `Err(())` means invalid selector
|
||||
fn parse_simple_selectors<I: Iterator<ComponentValue>>(
|
||||
iter: &mut Iter<I>, namespaces: &NamespaceMap)
|
||||
-> Result<(Vec<SimpleSelector>, Option<PseudoElement>), ()> {
|
||||
let mut empty = true;
|
||||
let mut simple_selectors = match try!(parse_type_selector(iter, namespaces)) {
|
||||
None => vec![],
|
||||
Some(s) => { empty = false; s }
|
||||
};
|
||||
|
||||
let mut pseudo_element = None;
|
||||
loop {
|
||||
match try!(parse_one_simple_selector(iter, namespaces, /* inside_negation = */ false)) {
|
||||
None => break,
|
||||
Some(SimpleSelectorResult(s)) => { simple_selectors.push(s); empty = false },
|
||||
Some(PseudoElementResult(p)) => { pseudo_element = Some(p); empty = false; break },
|
||||
}
|
||||
}
|
||||
if empty { Err(()) } // An empty selector is invalid
|
||||
else { Ok((simple_selectors, pseudo_element)) }
|
||||
}
|
||||
|
||||
|
||||
/// * `Err(())`: Invalid selector, abort
|
||||
/// * `Ok(None)`: Not a type selector, could be something else. `iter` was not consumed.
|
||||
|
@ -310,67 +223,6 @@ enum SimpleSelectorParseResult {
|
|||
PseudoElementResult(PseudoElement),
|
||||
}
|
||||
|
||||
/// Parse a simple selector other than a type selector.
|
||||
///
|
||||
/// * `Err(())`: Invalid selector, abort
|
||||
/// * `Ok(None)`: Not a simple selector, could be something else. `iter` was not consumed.
|
||||
/// * `Ok(Some(_))`: Parsed a simple selector or pseudo-element
|
||||
fn parse_one_simple_selector<I: Iterator<ComponentValue>>(
|
||||
iter: &mut Iter<I>, namespaces: &NamespaceMap, inside_negation: bool)
|
||||
-> Result<Option<SimpleSelectorParseResult>, ()> {
|
||||
match iter.peek() {
|
||||
Some(&IDHash(_)) => match iter.next() {
|
||||
Some(IDHash(id)) => Ok(Some(SimpleSelectorResult(
|
||||
IDSelector(Atom::from_slice(id.as_slice()))))),
|
||||
_ => panic!("Implementation error, this should not happen."),
|
||||
},
|
||||
Some(&Delim('.')) => {
|
||||
iter.next();
|
||||
match iter.next() {
|
||||
Some(Ident(class)) => Ok(Some(SimpleSelectorResult(
|
||||
ClassSelector(Atom::from_slice(class.as_slice()))))),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
Some(&SquareBracketBlock(_)) => match iter.next() {
|
||||
Some(SquareBracketBlock(content))
|
||||
=> Ok(Some(SimpleSelectorResult(try!(parse_attribute_selector(content, namespaces))))),
|
||||
_ => panic!("Implementation error, this should not happen."),
|
||||
},
|
||||
Some(&Colon) => {
|
||||
iter.next();
|
||||
match iter.next() {
|
||||
Some(Ident(name)) => match parse_simple_pseudo_class(name.as_slice()) {
|
||||
Err(()) => {
|
||||
match name.as_slice().to_ascii_lower().as_slice() {
|
||||
// Supported CSS 2.1 pseudo-elements only.
|
||||
// ** Do not add to this list! **
|
||||
"before" => Ok(Some(PseudoElementResult(Before))),
|
||||
"after" => Ok(Some(PseudoElementResult(After))),
|
||||
// "first-line" => PseudoElementResult(FirstLine),
|
||||
// "first-letter" => PseudoElementResult(FirstLetter),
|
||||
_ => Err(())
|
||||
}
|
||||
},
|
||||
Ok(result) => Ok(Some(SimpleSelectorResult(result))),
|
||||
},
|
||||
Some(Function(name, arguments))
|
||||
=> Ok(Some(SimpleSelectorResult(try!(parse_functional_pseudo_class(
|
||||
name, arguments, namespaces, inside_negation))))),
|
||||
Some(Colon) => {
|
||||
match iter.next() {
|
||||
Some(Ident(name))
|
||||
=> Ok(Some(PseudoElementResult(try!(parse_pseudo_element(name))))),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// * `Err(())`: Invalid selector, abort
|
||||
/// * `Ok(None)`: Not a simple selector, could be something else. `iter` was not consumed.
|
||||
|
@ -490,8 +342,224 @@ fn parse_attribute_flags<I: Iterator<ComponentValue>>(iter: &mut Iter<I>)
|
|||
}
|
||||
}
|
||||
|
||||
pub fn parse_selector_list_from_str(context: &ParserContext, input: &str)
|
||||
-> Result<SelectorList,()> {
|
||||
let namespaces = NamespaceMap::new();
|
||||
let iter = tokenize(input).map(|(token, _)| token);
|
||||
parse_selector_list(context, iter, &namespaces).map(|s| SelectorList { selectors: s })
|
||||
}
|
||||
|
||||
fn parse_simple_pseudo_class(name: &str) -> Result<SimpleSelector, ()> {
|
||||
/// Parse a comma-separated list of Selectors.
|
||||
/// aka Selector Group in http://www.w3.org/TR/css3-selectors/#grouping
|
||||
///
|
||||
/// Return the Selectors or None if there is an invalid selector.
|
||||
pub fn parse_selector_list<I>(context: &ParserContext, iter: I, namespaces: &NamespaceMap)
|
||||
-> Result<Vec<Selector>,()>
|
||||
where I: Iterator<ComponentValue> {
|
||||
let iter = &mut iter.peekable();
|
||||
let mut results = vec![try!(parse_selector(context, iter, namespaces))];
|
||||
|
||||
loop {
|
||||
skip_whitespace(iter);
|
||||
match iter.peek() {
|
||||
None => break, // EOF
|
||||
Some(&Comma) => {
|
||||
iter.next();
|
||||
}
|
||||
_ => return Err(()),
|
||||
}
|
||||
results.push(try!(parse_selector(context, iter, namespaces)));
|
||||
}
|
||||
Ok(results)
|
||||
}
|
||||
/// Build up a Selector.
|
||||
/// selector : simple_selector_sequence [ combinator simple_selector_sequence ]* ;
|
||||
///
|
||||
/// `Err` means invalid selector.
|
||||
fn parse_selector<I>(context: &ParserContext, iter: &mut Iter<I>, namespaces: &NamespaceMap)
|
||||
-> Result<Selector,()>
|
||||
where I: Iterator<ComponentValue> {
|
||||
let (first, mut pseudo_element) = try!(parse_simple_selectors(context, iter, namespaces));
|
||||
let mut compound = CompoundSelector{ simple_selectors: first, next: None };
|
||||
|
||||
while pseudo_element.is_none() {
|
||||
let any_whitespace = skip_whitespace(iter);
|
||||
let combinator = match iter.peek() {
|
||||
None => break, // EOF
|
||||
Some(&Comma) => break,
|
||||
Some(&Delim('>')) => { iter.next(); Child },
|
||||
Some(&Delim('+')) => { iter.next(); NextSibling },
|
||||
Some(&Delim('~')) => { iter.next(); LaterSibling },
|
||||
Some(_) => {
|
||||
if any_whitespace { Descendant }
|
||||
else { return Err(()) }
|
||||
}
|
||||
};
|
||||
let (simple_selectors, pseudo) = try!(parse_simple_selectors(context, iter, namespaces));
|
||||
compound = CompoundSelector {
|
||||
simple_selectors: simple_selectors,
|
||||
next: Some((box compound, combinator))
|
||||
};
|
||||
pseudo_element = pseudo;
|
||||
}
|
||||
Ok(Selector {
|
||||
specificity: compute_specificity(&compound, &pseudo_element),
|
||||
compound_selectors: Arc::new(compound),
|
||||
pseudo_element: pseudo_element,
|
||||
})
|
||||
}
|
||||
|
||||
/// Level 3: Parse **one** simple_selector
|
||||
fn parse_negation(context: &ParserContext,
|
||||
arguments: Vec<ComponentValue>,
|
||||
namespaces: &NamespaceMap)
|
||||
-> Result<SimpleSelector,()> {
|
||||
let iter = &mut arguments.into_iter().peekable();
|
||||
match try!(parse_type_selector(iter, namespaces)) {
|
||||
Some(type_selector) => Ok(Negation(type_selector)),
|
||||
None => {
|
||||
match try!(parse_one_simple_selector(context,
|
||||
iter,
|
||||
namespaces,
|
||||
/* inside_negation = */ true)) {
|
||||
Some(SimpleSelectorResult(simple_selector)) => {
|
||||
Ok(Negation(vec![simple_selector]))
|
||||
}
|
||||
_ => Err(())
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// simple_selector_sequence
|
||||
/// : [ type_selector | universal ] [ HASH | class | attrib | pseudo | negation ]*
|
||||
/// | [ HASH | class | attrib | pseudo | negation ]+
|
||||
///
|
||||
/// `Err(())` means invalid selector
|
||||
fn parse_simple_selectors<I>(context: &ParserContext,
|
||||
iter: &mut Iter<I>,
|
||||
namespaces: &NamespaceMap)
|
||||
-> Result<(Vec<SimpleSelector>, Option<PseudoElement>),()>
|
||||
where I: Iterator<ComponentValue> {
|
||||
let mut empty = true;
|
||||
let mut simple_selectors = match try!(parse_type_selector(iter, namespaces)) {
|
||||
None => vec![],
|
||||
Some(s) => { empty = false; s }
|
||||
};
|
||||
|
||||
let mut pseudo_element = None;
|
||||
loop {
|
||||
match try!(parse_one_simple_selector(context,
|
||||
iter,
|
||||
namespaces,
|
||||
/* inside_negation = */ false)) {
|
||||
None => break,
|
||||
Some(SimpleSelectorResult(s)) => { simple_selectors.push(s); empty = false },
|
||||
Some(PseudoElementResult(p)) => { pseudo_element = Some(p); empty = false; break },
|
||||
}
|
||||
}
|
||||
if empty {
|
||||
// An empty selector is invalid.
|
||||
Err(())
|
||||
} else {
|
||||
Ok((simple_selectors, pseudo_element))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_functional_pseudo_class(context: &ParserContext,
|
||||
name: String,
|
||||
arguments: Vec<ComponentValue>,
|
||||
namespaces: &NamespaceMap,
|
||||
inside_negation: bool)
|
||||
-> Result<SimpleSelector,()> {
|
||||
match name.as_slice().to_ascii_lower().as_slice() {
|
||||
// "lang" => parse_lang(arguments),
|
||||
"nth-child" => parse_nth(arguments.as_slice()).map(|(a, b)| NthChild(a, b)),
|
||||
"nth-last-child" => parse_nth(arguments.as_slice()).map(|(a, b)| NthLastChild(a, b)),
|
||||
"nth-of-type" => parse_nth(arguments.as_slice()).map(|(a, b)| NthOfType(a, b)),
|
||||
"nth-last-of-type" => parse_nth(arguments.as_slice()).map(|(a, b)| NthLastOfType(a, b)),
|
||||
"not" => {
|
||||
if inside_negation {
|
||||
Err(())
|
||||
} else {
|
||||
parse_negation(context, arguments, namespaces)
|
||||
}
|
||||
}
|
||||
_ => Err(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a simple selector other than a type selector.
|
||||
///
|
||||
/// * `Err(())`: Invalid selector, abort
|
||||
/// * `Ok(None)`: Not a simple selector, could be something else. `iter` was not consumed.
|
||||
/// * `Ok(Some(_))`: Parsed a simple selector or pseudo-element
|
||||
fn parse_one_simple_selector<I>(context: &ParserContext,
|
||||
iter: &mut Iter<I>,
|
||||
namespaces: &NamespaceMap,
|
||||
inside_negation: bool)
|
||||
-> Result<Option<SimpleSelectorParseResult>,()>
|
||||
where I: Iterator<ComponentValue> {
|
||||
match iter.peek() {
|
||||
Some(&IDHash(_)) => match iter.next() {
|
||||
Some(IDHash(id)) => Ok(Some(SimpleSelectorResult(
|
||||
IDSelector(Atom::from_slice(id.as_slice()))))),
|
||||
_ => panic!("Implementation error, this should not happen."),
|
||||
},
|
||||
Some(&Delim('.')) => {
|
||||
iter.next();
|
||||
match iter.next() {
|
||||
Some(Ident(class)) => Ok(Some(SimpleSelectorResult(
|
||||
ClassSelector(Atom::from_slice(class.as_slice()))))),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
Some(&SquareBracketBlock(_)) => match iter.next() {
|
||||
Some(SquareBracketBlock(content))
|
||||
=> Ok(Some(SimpleSelectorResult(try!(parse_attribute_selector(content, namespaces))))),
|
||||
_ => panic!("Implementation error, this should not happen."),
|
||||
},
|
||||
Some(&Colon) => {
|
||||
iter.next();
|
||||
match iter.next() {
|
||||
Some(Ident(name)) => match parse_simple_pseudo_class(context, name.as_slice()) {
|
||||
Err(()) => {
|
||||
match name.as_slice().to_ascii_lower().as_slice() {
|
||||
// Supported CSS 2.1 pseudo-elements only.
|
||||
// ** Do not add to this list! **
|
||||
"before" => Ok(Some(PseudoElementResult(Before))),
|
||||
"after" => Ok(Some(PseudoElementResult(After))),
|
||||
// "first-line" => PseudoElementResult(FirstLine),
|
||||
// "first-letter" => PseudoElementResult(FirstLetter),
|
||||
_ => Err(())
|
||||
}
|
||||
},
|
||||
Ok(result) => Ok(Some(SimpleSelectorResult(result))),
|
||||
},
|
||||
Some(Function(name, arguments))
|
||||
=> {
|
||||
Ok(Some(SimpleSelectorResult(try!(parse_functional_pseudo_class(
|
||||
context,
|
||||
name,
|
||||
arguments,
|
||||
namespaces,
|
||||
inside_negation)))))
|
||||
}
|
||||
Some(Colon) => {
|
||||
match iter.next() {
|
||||
Some(Ident(name))
|
||||
=> Ok(Some(PseudoElementResult(try!(parse_pseudo_element(name))))),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_simple_pseudo_class(context: &ParserContext, name: &str) -> Result<SimpleSelector,()> {
|
||||
match name.to_ascii_lower().as_slice() {
|
||||
"any-link" => Ok(AnyLink),
|
||||
"link" => Ok(Link),
|
||||
|
@ -507,31 +575,12 @@ fn parse_simple_pseudo_class(name: &str) -> Result<SimpleSelector, ()> {
|
|||
"first-of-type" => Ok(FirstOfType),
|
||||
"last-of-type" => Ok(LastOfType),
|
||||
"only-of-type" => Ok(OnlyOfType),
|
||||
"-servo-nonzero-border" => {
|
||||
// TODO(pcwalton): Have some mechanism whereby we forbid Web content from using this.
|
||||
Ok(ServoNonzeroBorder)
|
||||
}
|
||||
// "empty" => Ok(Empty),
|
||||
"-servo-nonzero-border" if context.origin == UserAgentOrigin => Ok(ServoNonzeroBorder),
|
||||
// "empty" => Ok(Empty),
|
||||
_ => Err(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn parse_functional_pseudo_class(name: String, arguments: Vec<ComponentValue>,
|
||||
namespaces: &NamespaceMap, inside_negation: bool)
|
||||
-> Result<SimpleSelector, ()> {
|
||||
match name.as_slice().to_ascii_lower().as_slice() {
|
||||
// "lang" => parse_lang(arguments),
|
||||
"nth-child" => parse_nth(arguments.as_slice()).map(|(a, b)| NthChild(a, b)),
|
||||
"nth-last-child" => parse_nth(arguments.as_slice()).map(|(a, b)| NthLastChild(a, b)),
|
||||
"nth-of-type" => parse_nth(arguments.as_slice()).map(|(a, b)| NthOfType(a, b)),
|
||||
"nth-last-of-type" => parse_nth(arguments.as_slice()).map(|(a, b)| NthLastOfType(a, b)),
|
||||
"not" => if inside_negation { Err(()) } else { parse_negation(arguments, namespaces) },
|
||||
_ => Err(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn parse_pseudo_element(name: String) -> Result<PseudoElement, ()> {
|
||||
match name.as_slice().to_ascii_lower().as_slice() {
|
||||
// All supported pseudo-elements
|
||||
|
@ -556,21 +605,6 @@ fn parse_pseudo_element(name: String) -> Result<PseudoElement, ()> {
|
|||
//}
|
||||
|
||||
|
||||
/// Level 3: Parse **one** simple_selector
|
||||
fn parse_negation(arguments: Vec<ComponentValue>, namespaces: &NamespaceMap)
|
||||
-> Result<SimpleSelector, ()> {
|
||||
let iter = &mut arguments.into_iter().peekable();
|
||||
match try!(parse_type_selector(iter, namespaces)) {
|
||||
Some(type_selector) => Ok(Negation(type_selector)),
|
||||
None => {
|
||||
match try!(parse_one_simple_selector(iter, namespaces, /* inside_negation = */ true)) {
|
||||
Some(SimpleSelectorResult(simple_selector)) => Ok(Negation(vec![simple_selector])),
|
||||
_ => Err(())
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Assuming the next token is an ident, consume it and return its value
|
||||
#[inline]
|
||||
|
@ -598,6 +632,7 @@ mod tests {
|
|||
use sync::Arc;
|
||||
use cssparser;
|
||||
use namespaces::NamespaceMap;
|
||||
use selector_matching::AuthorOrigin;
|
||||
use string_cache::Atom;
|
||||
use super::*;
|
||||
|
||||
|
@ -606,7 +641,10 @@ mod tests {
|
|||
}
|
||||
|
||||
fn parse_ns(input: &str, namespaces: &NamespaceMap) -> Result<Vec<Selector>, ()> {
|
||||
parse_selector_list(cssparser::tokenize(input).map(|(v, _)| v), namespaces)
|
||||
let context = ParserContext {
|
||||
origin: AuthorOrigin,
|
||||
};
|
||||
parse_selector_list(&context, cssparser::tokenize(input).map(|(v, _)| v), namespaces)
|
||||
}
|
||||
|
||||
fn specificity(a: u32, b: u32, c: u32) -> u32 {
|
||||
|
|
|
@ -10,12 +10,11 @@ use encoding::EncodingRef;
|
|||
|
||||
use cssparser::{decode_stylesheet_bytes, tokenize, parse_stylesheet_rules, ToCss};
|
||||
use cssparser::ast::*;
|
||||
use selectors;
|
||||
use selectors::{mod, ParserContext};
|
||||
use properties;
|
||||
use errors::{ErrorLoggerIterator, log_css_error};
|
||||
use namespaces::{NamespaceMap, parse_namespace_rule};
|
||||
use media_queries::{Device, MediaRule, parse_media_rule};
|
||||
use media_queries;
|
||||
use media_queries::{mod, Device, MediaRule};
|
||||
use font_face::{FontFaceRule, Source, parse_font_face_rule, iter_font_face_rules_inner};
|
||||
use selector_matching::StylesheetOrigin;
|
||||
|
||||
|
@ -53,9 +52,12 @@ impl Stylesheet {
|
|||
Stylesheet::from_bytes(bytes.as_slice(), base_url, protocol_encoding_label, environment_encoding, origin)
|
||||
}
|
||||
|
||||
pub fn from_bytes(
|
||||
bytes: &[u8], base_url: Url, protocol_encoding_label: Option<&str>,
|
||||
environment_encoding: Option<EncodingRef>, origin: StylesheetOrigin) -> Stylesheet {
|
||||
pub fn from_bytes(bytes: &[u8],
|
||||
base_url: Url,
|
||||
protocol_encoding_label: Option<&str>,
|
||||
environment_encoding: Option<EncodingRef>,
|
||||
origin: StylesheetOrigin)
|
||||
-> Stylesheet {
|
||||
// TODO: bytes.as_slice could be bytes.container_as_bytes()
|
||||
let (string, _) = decode_stylesheet_bytes(
|
||||
bytes.as_slice(), protocol_encoding_label, environment_encoding);
|
||||
|
@ -67,6 +69,11 @@ impl Stylesheet {
|
|||
static STATE_IMPORTS: uint = 2;
|
||||
static STATE_NAMESPACES: uint = 3;
|
||||
static STATE_BODY: uint = 4;
|
||||
|
||||
let parser_context = ParserContext {
|
||||
origin: origin,
|
||||
};
|
||||
|
||||
let mut state: uint = STATE_CHARSET;
|
||||
|
||||
let mut rules = vec!();
|
||||
|
@ -77,7 +84,7 @@ impl Stylesheet {
|
|||
match rule {
|
||||
QualifiedRule_(rule) => {
|
||||
next_state = STATE_BODY;
|
||||
parse_style_rule(rule, &mut rules, &namespaces, &base_url)
|
||||
parse_style_rule(&parser_context, rule, &mut rules, &namespaces, &base_url)
|
||||
},
|
||||
AtRule_(rule) => {
|
||||
let lower_name = rule.name.as_slice().to_ascii_lower();
|
||||
|
@ -114,7 +121,12 @@ impl Stylesheet {
|
|||
},
|
||||
_ => {
|
||||
next_state = STATE_BODY;
|
||||
parse_nested_at_rule(lower_name.as_slice(), rule, &mut rules, &namespaces, &base_url)
|
||||
parse_nested_at_rule(&parser_context,
|
||||
lower_name.as_slice(),
|
||||
rule,
|
||||
&mut rules,
|
||||
&namespaces,
|
||||
&base_url)
|
||||
},
|
||||
}
|
||||
},
|
||||
|
@ -128,13 +140,36 @@ impl Stylesheet {
|
|||
}
|
||||
}
|
||||
|
||||
// lower_name is passed explicitly to avoid computing it twice.
|
||||
pub fn parse_nested_at_rule(context: &ParserContext,
|
||||
lower_name: &str,
|
||||
rule: AtRule,
|
||||
parent_rules: &mut Vec<CSSRule>,
|
||||
namespaces: &NamespaceMap,
|
||||
base_url: &Url) {
|
||||
match lower_name {
|
||||
"media" => {
|
||||
media_queries::parse_media_rule(context, rule, parent_rules, namespaces, base_url)
|
||||
}
|
||||
"font-face" => parse_font_face_rule(rule, parent_rules, base_url),
|
||||
_ => log_css_error(rule.location,
|
||||
format!("Unsupported at-rule: @{:s}", lower_name).as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_style_rule(rule: QualifiedRule, parent_rules: &mut Vec<CSSRule>,
|
||||
namespaces: &NamespaceMap, base_url: &Url) {
|
||||
let QualifiedRule { location, prelude, block} = rule;
|
||||
pub fn parse_style_rule(context: &ParserContext,
|
||||
rule: QualifiedRule,
|
||||
parent_rules: &mut Vec<CSSRule>,
|
||||
namespaces: &NamespaceMap,
|
||||
base_url: &Url) {
|
||||
let QualifiedRule {
|
||||
location,
|
||||
prelude,
|
||||
block
|
||||
} = rule;
|
||||
// FIXME: avoid doing this for valid selectors
|
||||
let serialized = prelude.iter().to_css();
|
||||
match selectors::parse_selector_list(prelude.into_iter(), namespaces) {
|
||||
match selectors::parse_selector_list(context, prelude.into_iter(), namespaces) {
|
||||
Ok(selectors) => parent_rules.push(CSSStyleRule(StyleRule{
|
||||
selectors: selectors,
|
||||
declarations: properties::parse_property_declaration_list(block.into_iter(), base_url)
|
||||
|
@ -144,19 +179,6 @@ pub fn parse_style_rule(rule: QualifiedRule, parent_rules: &mut Vec<CSSRule>,
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// lower_name is passed explicitly to avoid computing it twice.
|
||||
pub fn parse_nested_at_rule(lower_name: &str, rule: AtRule,
|
||||
parent_rules: &mut Vec<CSSRule>, namespaces: &NamespaceMap, base_url: &Url) {
|
||||
match lower_name {
|
||||
"media" => parse_media_rule(rule, parent_rules, namespaces, base_url),
|
||||
"font-face" => parse_font_face_rule(rule, parent_rules, base_url),
|
||||
_ => log_css_error(rule.location,
|
||||
format!("Unsupported at-rule: @{:s}", lower_name).as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn iter_style_rules<'a>(rules: &[CSSRule], device: &media_queries::Device,
|
||||
callback: |&StyleRule|) {
|
||||
for rule in rules.iter() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue