use std::cmp::Ordering;
use selectors::context::{MatchingContext, SelectorCaches};
use super::declaration::Declaration;
use super::parse::{CssRule, Origin, Specificity, Stylesheet};
use super::style_pool::StylePool;
use super::types::ComputedStyle;
use crate::dom::element_ref::ElementRef;
#[derive(Debug)]
struct MatchedRule<'a> {
declaration: &'a Declaration,
origin: Origin,
specificity: Specificity,
order: usize,
important: bool,
}
fn inherit_from_parent(parent: &ComputedStyle) -> ComputedStyle {
ComputedStyle {
font_size: parent.font_size,
font_weight: parent.font_weight,
font_style: parent.font_style,
font_variant: parent.font_variant,
font_family: parent.font_family.clone(),
color: parent.color,
text_align: parent.text_align,
text_indent: parent.text_indent,
line_height: parent.line_height,
letter_spacing: parent.letter_spacing,
word_spacing: parent.word_spacing,
text_transform: parent.text_transform,
hyphens: parent.hyphens,
text_decoration_underline: parent.text_decoration_underline,
text_decoration_line_through: parent.text_decoration_line_through,
underline_style: parent.underline_style,
underline_color: parent.underline_color,
overline: parent.overline,
list_style_type: parent.list_style_type,
list_style_position: parent.list_style_position,
visibility: parent.visibility,
language: parent.language.clone(),
..ComputedStyle::default()
}
}
pub fn compute_styles(
elem: ElementRef<'_>,
stylesheets: &[(Stylesheet, Origin)],
parent_style: Option<&ComputedStyle>,
_style_pool: &mut StylePool,
) -> ComputedStyle {
let mut matched: Vec<MatchedRule> = Vec::with_capacity(16);
let mut order = 0;
let mut caches = SelectorCaches::default();
for (stylesheet, origin) in stylesheets {
for rule in &stylesheet.rules {
if rule_matches_with_caches(elem, rule, &mut caches) {
for decl in &rule.declarations {
matched.push(MatchedRule {
declaration: decl,
origin: *origin,
specificity: rule.specificity,
order,
important: false,
});
order += 1;
}
for decl in &rule.important_declarations {
matched.push(MatchedRule {
declaration: decl,
origin: *origin,
specificity: rule.specificity,
order,
important: true,
});
order += 1;
}
}
}
}
if matched.len() > 1 {
matched.sort_unstable_by(|a, b| {
if a.important != b.important {
return b.important.cmp(&a.important);
}
let origin_cmp = a.origin.cmp(&b.origin);
if origin_cmp != Ordering::Equal {
return origin_cmp;
}
let spec_cmp = a.specificity.cmp(&b.specificity);
if spec_cmp != Ordering::Equal {
return spec_cmp;
}
a.order.cmp(&b.order)
});
}
let mut style = if let Some(parent) = parent_style {
inherit_from_parent(parent)
} else {
ComputedStyle::default()
};
for matched_rule in &matched {
apply_declaration(&mut style, matched_rule.declaration);
}
style
}
fn rule_matches_with_caches(
elem: ElementRef<'_>,
rule: &CssRule,
caches: &mut SelectorCaches,
) -> bool {
let mut context = MatchingContext::new(
selectors::matching::MatchingMode::Normal,
None,
caches,
selectors::context::QuirksMode::NoQuirks,
selectors::matching::NeedsSelectorFlags::No,
selectors::matching::MatchingForInvalidation::No,
);
rule.selectors.iter().any(|selector| {
selectors::matching::matches_selector(selector, 0, None, &elem, &mut context)
})
}
fn apply_declaration(style: &mut ComputedStyle, decl: &Declaration) {
match decl {
Declaration::Color(c) => style.color = Some(*c),
Declaration::BackgroundColor(c) => style.background_color = Some(*c),
Declaration::FontFamily(s) => style.font_family = Some(s.clone()),
Declaration::FontSize(l) => style.font_size = *l,
Declaration::FontWeight(w) => style.font_weight = *w,
Declaration::FontStyle(s) => style.font_style = *s,
Declaration::FontVariant(v) => style.font_variant = *v,
Declaration::TextAlign(a) => style.text_align = *a,
Declaration::TextIndent(l) => style.text_indent = *l,
Declaration::LineHeight(l) => style.line_height = *l,
Declaration::LetterSpacing(l) => style.letter_spacing = *l,
Declaration::WordSpacing(l) => style.word_spacing = *l,
Declaration::TextTransform(t) => style.text_transform = *t,
Declaration::Hyphens(h) => style.hyphens = *h,
Declaration::WhiteSpace(ws) => style.white_space = *ws,
Declaration::VerticalAlign(v) => style.vertical_align = *v,
Declaration::TextDecoration(d) => {
style.text_decoration_underline = d.underline;
style.text_decoration_line_through = d.line_through;
}
Declaration::TextDecorationStyle(s) => style.underline_style = *s,
Declaration::TextDecorationColor(c) => style.underline_color = Some(*c),
Declaration::Margin(l) => {
style.margin_top = *l;
style.margin_right = *l;
style.margin_bottom = *l;
style.margin_left = *l;
}
Declaration::MarginTop(l) => style.margin_top = *l,
Declaration::MarginRight(l) => style.margin_right = *l,
Declaration::MarginBottom(l) => style.margin_bottom = *l,
Declaration::MarginLeft(l) => style.margin_left = *l,
Declaration::Padding(l) => {
style.padding_top = *l;
style.padding_right = *l;
style.padding_bottom = *l;
style.padding_left = *l;
}
Declaration::PaddingTop(l) => style.padding_top = *l,
Declaration::PaddingRight(l) => style.padding_right = *l,
Declaration::PaddingBottom(l) => style.padding_bottom = *l,
Declaration::PaddingLeft(l) => style.padding_left = *l,
Declaration::Width(l) => style.width = *l,
Declaration::Height(l) => style.height = *l,
Declaration::MaxWidth(l) => style.max_width = *l,
Declaration::MaxHeight(l) => style.max_height = *l,
Declaration::MinWidth(l) => style.min_width = *l,
Declaration::MinHeight(l) => style.min_height = *l,
Declaration::Display(d) => style.display = *d,
Declaration::Float(f) => style.float = *f,
Declaration::Clear(c) => style.clear = *c,
Declaration::Visibility(v) => style.visibility = *v,
Declaration::BoxSizing(bs) => style.box_sizing = *bs,
Declaration::Orphans(n) => style.orphans = *n,
Declaration::Widows(n) => style.widows = *n,
Declaration::WordBreak(wb) => style.word_break = *wb,
Declaration::OverflowWrap(ow) => style.overflow_wrap = *ow,
Declaration::BreakBefore(b) => style.break_before = *b,
Declaration::BreakAfter(b) => style.break_after = *b,
Declaration::BreakInside(b) => style.break_inside = *b,
Declaration::BorderStyle(s) => {
style.border_style_top = *s;
style.border_style_right = *s;
style.border_style_bottom = *s;
style.border_style_left = *s;
}
Declaration::BorderTopStyle(s) => style.border_style_top = *s,
Declaration::BorderRightStyle(s) => style.border_style_right = *s,
Declaration::BorderBottomStyle(s) => style.border_style_bottom = *s,
Declaration::BorderLeftStyle(s) => style.border_style_left = *s,
Declaration::BorderWidth(l) => {
style.border_width_top = *l;
style.border_width_right = *l;
style.border_width_bottom = *l;
style.border_width_left = *l;
}
Declaration::BorderTopWidth(l) => style.border_width_top = *l,
Declaration::BorderRightWidth(l) => style.border_width_right = *l,
Declaration::BorderBottomWidth(l) => style.border_width_bottom = *l,
Declaration::BorderLeftWidth(l) => style.border_width_left = *l,
Declaration::BorderColor(c) => {
style.border_color_top = Some(*c);
style.border_color_right = Some(*c);
style.border_color_bottom = Some(*c);
style.border_color_left = Some(*c);
}
Declaration::BorderTopColor(c) => style.border_color_top = Some(*c),
Declaration::BorderRightColor(c) => style.border_color_right = Some(*c),
Declaration::BorderBottomColor(c) => style.border_color_bottom = Some(*c),
Declaration::BorderLeftColor(c) => style.border_color_left = Some(*c),
Declaration::BorderRadius(l) => {
style.border_radius_top_left = *l;
style.border_radius_top_right = *l;
style.border_radius_bottom_left = *l;
style.border_radius_bottom_right = *l;
}
Declaration::BorderTopLeftRadius(l) => style.border_radius_top_left = *l,
Declaration::BorderTopRightRadius(l) => style.border_radius_top_right = *l,
Declaration::BorderBottomLeftRadius(l) => style.border_radius_bottom_left = *l,
Declaration::BorderBottomRightRadius(l) => style.border_radius_bottom_right = *l,
Declaration::ListStyleType(lst) => style.list_style_type = *lst,
Declaration::ListStylePosition(p) => style.list_style_position = *p,
Declaration::BorderCollapse(bc) => style.border_collapse = *bc,
Declaration::BorderSpacing(l) => style.border_spacing = *l,
}
}