mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 20:27:45 +00:00
LibWeb: Calculate specificity for special pseudo-classes
This fixes the specificity for :not(), :is() and :where(). Also, we now clamp the specificity numbers instead of letting them overflow, and I sprinkled in some spec comments for good measure.
This commit is contained in:
parent
993653317c
commit
f241827f6a
1 changed files with 50 additions and 6 deletions
|
@ -31,32 +31,76 @@ u32 Selector::specificity() const
|
||||||
if (m_specificity.has_value())
|
if (m_specificity.has_value())
|
||||||
return *m_specificity;
|
return *m_specificity;
|
||||||
|
|
||||||
unsigned ids = 0;
|
constexpr u32 ids_shift = 16;
|
||||||
unsigned tag_names = 0;
|
constexpr u32 classes_shift = 8;
|
||||||
unsigned classes = 0;
|
constexpr u32 tag_names_shift = 0;
|
||||||
|
constexpr u32 ids_mask = 0xff << ids_shift;
|
||||||
|
constexpr u32 classes_mask = 0xff << classes_shift;
|
||||||
|
constexpr u32 tag_names_mask = 0xff << tag_names_shift;
|
||||||
|
|
||||||
|
u32 ids = 0;
|
||||||
|
u32 classes = 0;
|
||||||
|
u32 tag_names = 0;
|
||||||
|
|
||||||
for (auto& list : m_compound_selectors) {
|
for (auto& list : m_compound_selectors) {
|
||||||
for (auto& simple_selector : list.simple_selectors) {
|
for (auto& simple_selector : list.simple_selectors) {
|
||||||
switch (simple_selector.type) {
|
switch (simple_selector.type) {
|
||||||
case SimpleSelector::Type::Id:
|
case SimpleSelector::Type::Id:
|
||||||
|
// count the number of ID selectors in the selector (= A)
|
||||||
++ids;
|
++ids;
|
||||||
break;
|
break;
|
||||||
case SimpleSelector::Type::Class:
|
case SimpleSelector::Type::Class:
|
||||||
case SimpleSelector::Type::Attribute:
|
case SimpleSelector::Type::Attribute:
|
||||||
case SimpleSelector::Type::PseudoClass:
|
// count the number of class selectors, attributes selectors, and pseudo-classes in the selector (= B)
|
||||||
++classes;
|
++classes;
|
||||||
break;
|
break;
|
||||||
|
case SimpleSelector::Type::PseudoClass:
|
||||||
|
switch (simple_selector.pseudo_class.type) {
|
||||||
|
case SimpleSelector::PseudoClass::Type::Is:
|
||||||
|
case SimpleSelector::PseudoClass::Type::Not: {
|
||||||
|
// The specificity of an :is(), :not(), or :has() pseudo-class is replaced by the
|
||||||
|
// specificity of the most specific complex selector in its selector list argument.
|
||||||
|
u32 max_selector_list_argument_specificity = 0;
|
||||||
|
for (auto const& complex_selector : simple_selector.pseudo_class.argument_selector_list) {
|
||||||
|
max_selector_list_argument_specificity = max(max_selector_list_argument_specificity, complex_selector.specificity());
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 child_ids = (max_selector_list_argument_specificity & ids_mask) >> ids_shift;
|
||||||
|
u32 child_classes = (max_selector_list_argument_specificity & classes_mask) >> classes_shift;
|
||||||
|
u32 child_tag_names = (max_selector_list_argument_specificity & tag_names_mask) >> tag_names_shift;
|
||||||
|
|
||||||
|
ids += child_ids;
|
||||||
|
classes += child_classes;
|
||||||
|
tag_names += child_tag_names;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SimpleSelector::PseudoClass::Type::Where:
|
||||||
|
// The specificity of a :where() pseudo-class is replaced by zero.
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
++classes;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case SimpleSelector::Type::TagName:
|
case SimpleSelector::Type::TagName:
|
||||||
case SimpleSelector::Type::PseudoElement:
|
case SimpleSelector::Type::PseudoElement:
|
||||||
|
// count the number of type selectors and pseudo-elements in the selector (= C)
|
||||||
++tag_names;
|
++tag_names;
|
||||||
break;
|
break;
|
||||||
default:
|
case SimpleSelector::Type::Universal:
|
||||||
|
// ignore the universal selector
|
||||||
|
break;
|
||||||
|
case SimpleSelector::Type::Invalid:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_specificity = ids * 0x10000 + classes * 0x100 + tag_names;
|
// Due to storage limitations, implementations may have limitations on the size of A, B, or C.
|
||||||
|
// If so, values higher than the limit must be clamped to that limit, and not overflow.
|
||||||
|
m_specificity = (min(ids, 0xff) << ids_shift)
|
||||||
|
+ (min(classes, 0xff) << classes_shift)
|
||||||
|
+ (min(tag_names, 0xff) << tag_names_shift);
|
||||||
|
|
||||||
return *m_specificity;
|
return *m_specificity;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue