use cssparser::{
AtRuleParser, ParseError, Parser, ParserInput, QualifiedRuleParser, RuleBodyItemParser,
RuleBodyParser, StyleSheetParser,
};
use selectors::parser::Selector;
use crate::dom::element_ref::BokoSelectors;
use crate::model::FontFace;
use crate::style::Declaration;
use super::font::parse_font_face_block;
#[derive(Debug, Default, Clone)]
pub struct Stylesheet {
pub rules: Vec<CssRule>,
pub font_faces: Vec<FontFace>,
}
#[derive(Debug, Clone)]
pub struct CssRule {
pub selectors: Vec<Selector<BokoSelectors>>,
pub declarations: Vec<Declaration>,
pub important_declarations: Vec<Declaration>,
pub specificity: Specificity,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Specificity {
pub ids: u16,
pub classes: u16,
pub elements: u16,
}
impl Specificity {
pub fn from_selector(selector: &Selector<BokoSelectors>) -> Self {
let spec = selector.specificity();
Self {
ids: ((spec >> 20) & 0x3FF) as u16,
classes: ((spec >> 10) & 0x3FF) as u16,
elements: (spec & 0x3FF) as u16,
}
}
}
impl Ord for Specificity {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.ids
.cmp(&other.ids)
.then(self.classes.cmp(&other.classes))
.then(self.elements.cmp(&other.elements))
}
}
impl PartialOrd for Specificity {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Origin {
UserAgent = 0,
Author = 1,
}
impl Stylesheet {
pub fn parse(css: &str) -> Self {
let mut input = ParserInput::new(css);
let mut parser = Parser::new(&mut input);
let mut rules = Vec::new();
let mut font_faces = Vec::new();
let mut rule_parser = TopLevelRuleParser {
rules: &mut rules,
font_faces: &mut font_faces,
};
let stylesheet_parser = StyleSheetParser::new(&mut parser, &mut rule_parser);
for result in stylesheet_parser {
let _ = result;
}
Self { rules, font_faces }
}
pub fn is_empty(&self) -> bool {
self.rules.is_empty()
}
}
struct TopLevelRuleParser<'a> {
rules: &'a mut Vec<CssRule>,
font_faces: &'a mut Vec<FontFace>,
}
struct FontFacePrelude;
impl<'i> AtRuleParser<'i> for TopLevelRuleParser<'_> {
type Prelude = FontFacePrelude;
type AtRule = ();
type Error = ();
fn parse_prelude<'t>(
&mut self,
name: cssparser::CowRcStr<'i>,
_input: &mut Parser<'i, 't>,
) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
if name.eq_ignore_ascii_case("font-face") {
Ok(FontFacePrelude)
} else {
Err(_input.new_custom_error(()))
}
}
fn parse_block<'t>(
&mut self,
_prelude: Self::Prelude,
_start: &cssparser::ParserState,
input: &mut Parser<'i, 't>,
) -> Result<Self::AtRule, ParseError<'i, Self::Error>> {
if let Some(font_face) = parse_font_face_block(input) {
self.font_faces.push(font_face);
}
Ok(())
}
}
impl<'i> QualifiedRuleParser<'i> for TopLevelRuleParser<'_> {
type Prelude = Vec<Selector<BokoSelectors>>;
type QualifiedRule = ();
type Error = ();
fn parse_prelude<'t>(
&mut self,
input: &mut Parser<'i, 't>,
) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
parse_selector_list(input)
}
fn parse_block<'t>(
&mut self,
prelude: Self::Prelude,
_start: &cssparser::ParserState,
input: &mut Parser<'i, 't>,
) -> Result<Self::QualifiedRule, ParseError<'i, Self::Error>> {
let specificity = prelude
.first()
.map(Specificity::from_selector)
.unwrap_or_default();
let mut declarations = Vec::new();
let mut important_declarations = Vec::new();
let mut decl_parser = DeclarationListParser {
declarations: &mut declarations,
important_declarations: &mut important_declarations,
};
for result in RuleBodyParser::new(input, &mut decl_parser) {
let _ = result;
}
self.rules.push(CssRule {
selectors: prelude,
declarations,
important_declarations,
specificity,
});
Ok(())
}
}
fn parse_selector_list<'i>(
parser: &mut Parser<'i, '_>,
) -> Result<Vec<Selector<BokoSelectors>>, ParseError<'i, ()>> {
let location = parser.current_source_location();
let selectors = selectors::parser::SelectorList::parse(
&BokoSelectors,
parser,
selectors::parser::ParseRelative::No,
)
.map_err(|_| location.new_custom_error(()))?;
Ok(selectors.slice().to_vec())
}
struct DeclarationListParser<'a> {
declarations: &'a mut Vec<Declaration>,
important_declarations: &'a mut Vec<Declaration>,
}
impl<'i> cssparser::AtRuleParser<'i> for DeclarationListParser<'_> {
type Prelude = ();
type AtRule = ();
type Error = ();
fn parse_prelude<'t>(
&mut self,
_name: cssparser::CowRcStr<'i>,
_input: &mut Parser<'i, 't>,
) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
Err(_input.new_custom_error(()))
}
fn parse_block<'t>(
&mut self,
_prelude: Self::Prelude,
_start: &cssparser::ParserState,
_input: &mut Parser<'i, 't>,
) -> Result<Self::AtRule, ParseError<'i, Self::Error>> {
Err(_input.new_custom_error(()))
}
}
impl<'i> cssparser::QualifiedRuleParser<'i> for DeclarationListParser<'_> {
type Prelude = ();
type QualifiedRule = ();
type Error = ();
fn parse_prelude<'t>(
&mut self,
_input: &mut Parser<'i, 't>,
) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
Err(_input.new_custom_error(()))
}
fn parse_block<'t>(
&mut self,
_prelude: Self::Prelude,
_start: &cssparser::ParserState,
_input: &mut Parser<'i, 't>,
) -> Result<Self::QualifiedRule, ParseError<'i, Self::Error>> {
Err(_input.new_custom_error(()))
}
}
impl<'i> cssparser::DeclarationParser<'i> for DeclarationListParser<'_> {
type Declaration = ();
type Error = ();
fn parse_value<'t>(
&mut self,
name: cssparser::CowRcStr<'i>,
input: &mut Parser<'i, 't>,
_start: &cssparser::ParserState,
) -> Result<Self::Declaration, ParseError<'i, Self::Error>> {
let decls = Declaration::parse(&name, input);
if !decls.is_empty() {
let important = input.try_parse(cssparser::parse_important).is_ok();
let target = if important {
&mut *self.important_declarations
} else {
&mut *self.declarations
};
target.extend(decls);
}
Ok(())
}
}
impl<'i> RuleBodyItemParser<'i, (), ()> for DeclarationListParser<'_> {
fn parse_declarations(&self) -> bool {
true
}
fn parse_qualified(&self) -> bool {
false
}
}