Browse Source

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.
Andreas Kling 5 years ago
parent
commit
384b5f15c9
2 changed files with 31 additions and 10 deletions
  1. 23 9
      Libraries/LibWeb/CSS/StyleResolver.cpp
  2. 8 1
      Libraries/LibWeb/CSS/StyleResolver.h

+ 23 - 9
Libraries/LibWeb/CSS/StyleResolver.cpp

@@ -66,19 +66,25 @@ void StyleResolver::for_each_stylesheet(Callback callback) const
     }
 }
 
-NonnullRefPtrVector<StyleRule> StyleResolver::collect_matching_rules(const Element& element) const
+Vector<MatchingRule> StyleResolver::collect_matching_rules(const Element& element) const
 {
-    NonnullRefPtrVector<StyleRule> matching_rules;
+    Vector<MatchingRule> 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<StyleProperties> 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<Vector<NonnullRefPtr<StyleRule>>&>(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);
         }
     }

+ 8 - 1
Libraries/LibWeb/CSS/StyleResolver.h

@@ -38,6 +38,13 @@ class ParentNode;
 class StyleRule;
 class StyleSheet;
 
+struct MatchingRule {
+    RefPtr<StyleRule> 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<StyleProperties> resolve_style(const Element&, const StyleProperties* parent_style) const;
 
-    NonnullRefPtrVector<StyleRule> collect_matching_rules(const Element&) const;
+    Vector<MatchingRule> collect_matching_rules(const Element&) const;
 
     static bool is_inherited_property(CSS::PropertyID);