From 384b5f15c943ad0b19fba2878ba3a8f7879ca413 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sat, 13 Jun 2020 00:44:26 +0200 Subject: LibWeb: Sort matched style rules by specificity, document order If two rules have equal specificity, they should be applied in the order in which we encountered them. --- Libraries/LibWeb/CSS/StyleResolver.cpp | 32 +++++++++++++++++++++++--------- Libraries/LibWeb/CSS/StyleResolver.h | 9 ++++++++- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/Libraries/LibWeb/CSS/StyleResolver.cpp b/Libraries/LibWeb/CSS/StyleResolver.cpp index ab6bd25cb8..80f0e8570f 100644 --- a/Libraries/LibWeb/CSS/StyleResolver.cpp +++ b/Libraries/LibWeb/CSS/StyleResolver.cpp @@ -66,19 +66,25 @@ void StyleResolver::for_each_stylesheet(Callback callback) const } } -NonnullRefPtrVector StyleResolver::collect_matching_rules(const Element& element) const +Vector StyleResolver::collect_matching_rules(const Element& element) const { - NonnullRefPtrVector matching_rules; + Vector matching_rules; + size_t style_sheet_index = 0; for_each_stylesheet([&](auto& sheet) { + size_t rule_index = 0; for (auto& rule : sheet.rules()) { + size_t selector_index = 0; for (auto& selector : rule.selectors()) { if (SelectorEngine::matches(selector, element)) { - matching_rules.append(rule); + matching_rules.append({ rule, style_sheet_index, rule_index, selector_index }); break; } + ++selector_index; } + ++rule_index; } + ++style_sheet_index; }); #ifdef HTML_DEBUG @@ -484,14 +490,22 @@ NonnullRefPtr StyleResolver::resolve_style(const Element& eleme auto matching_rules = collect_matching_rules(element); - // FIXME: We need to look at the specificity of the matching *selector*, not just the matched *rule*! - // FIXME: It's really awkward that NonnullRefPtrVector cannot be quick_sort()'ed - quick_sort(reinterpret_cast>&>(matching_rules), [&](auto& a, auto& b) { - return a->selectors().first().specificity() < b->selectors().first().specificity(); + quick_sort(matching_rules, [&](MatchingRule& a, MatchingRule& b) { + auto& a_selector = a.rule->selectors()[a.selector_index]; + auto& b_selector = b.rule->selectors()[b.selector_index]; + if (a_selector.specificity() < b_selector.specificity()) + return true; + if (!(a_selector.specificity() == b_selector.specificity())) + return false; + if (a.style_sheet_index < b.style_sheet_index) + return true; + if (a.style_sheet_index > b.style_sheet_index) + return false; + return a.rule_index < b.rule_index; }); - for (auto& rule : matching_rules) { - for (auto& property : rule.declaration().properties()) { + for (auto& match : matching_rules) { + for (auto& property : match.rule->declaration().properties()) { set_property_expanding_shorthands(style, property.property_id, property.value, m_document); } } diff --git a/Libraries/LibWeb/CSS/StyleResolver.h b/Libraries/LibWeb/CSS/StyleResolver.h index 95e691ed7f..54fa2fa763 100644 --- a/Libraries/LibWeb/CSS/StyleResolver.h +++ b/Libraries/LibWeb/CSS/StyleResolver.h @@ -38,6 +38,13 @@ class ParentNode; class StyleRule; class StyleSheet; +struct MatchingRule { + RefPtr rule; + size_t style_sheet_index { 0 }; + size_t rule_index { 0 }; + size_t selector_index { 0 }; +}; + class StyleResolver { public: explicit StyleResolver(Document&); @@ -48,7 +55,7 @@ public: NonnullRefPtr resolve_style(const Element&, const StyleProperties* parent_style) const; - NonnullRefPtrVector collect_matching_rules(const Element&) const; + Vector collect_matching_rules(const Element&) const; static bool is_inherited_property(CSS::PropertyID); -- cgit v1.2.3