aboutsummaryrefslogtreecommitdiffstats
path: root/components/selectors/builder.rs
diff options
context:
space:
mode:
authorBobby Holley <bobbyholley@gmail.com>2017-06-18 12:36:39 -0700
committerBobby Holley <bobbyholley@gmail.com>2017-06-20 11:59:11 -0700
commit1fc1d64e80398739346757a26357d0dc9c96a8fa (patch)
tree854363016a05b0edb13c74808729500217813eb5 /components/selectors/builder.rs
parent1d242ad760f17802c7849d34262f08986ce24bb4 (diff)
downloadservo-1fc1d64e80398739346757a26357d0dc9c96a8fa.tar.gz
servo-1fc1d64e80398739346757a26357d0dc9c96a8fa.zip
Hoist specificity computation into a new private builder module.
This patch doesn't modify any of the code because making a few things pub. I did this first to make the next patch easier to audit. MozReview-Commit-ID: 7PYxoS5bVGN
Diffstat (limited to 'components/selectors/builder.rs')
-rw-r--r--components/selectors/builder.rs141
1 files changed, 141 insertions, 0 deletions
diff --git a/components/selectors/builder.rs b/components/selectors/builder.rs
new file mode 100644
index 00000000000..9fd751b9bb8
--- /dev/null
+++ b/components/selectors/builder.rs
@@ -0,0 +1,141 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use parser::{Component, SelectorImpl, SelectorIter};
+use std::cmp;
+use std::ops::Add;
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub struct SpecificityAndFlags(pub u32);
+
+pub const HAS_PSEUDO_BIT: u32 = 1 << 30;
+
+impl SpecificityAndFlags {
+ pub fn specificity(&self) -> u32 {
+ self.0 & !HAS_PSEUDO_BIT
+ }
+
+ pub fn has_pseudo_element(&self) -> bool {
+ (self.0 & HAS_PSEUDO_BIT) != 0
+ }
+}
+
+const MAX_10BIT: u32 = (1u32 << 10) - 1;
+
+#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
+struct Specificity {
+ id_selectors: u32,
+ class_like_selectors: u32,
+ element_selectors: u32,
+}
+
+impl Add for Specificity {
+ type Output = Specificity;
+
+ fn add(self, rhs: Specificity) -> Specificity {
+ Specificity {
+ id_selectors: self.id_selectors + rhs.id_selectors,
+ class_like_selectors:
+ self.class_like_selectors + rhs.class_like_selectors,
+ element_selectors:
+ self.element_selectors + rhs.element_selectors,
+ }
+ }
+}
+
+impl Default for Specificity {
+ fn default() -> Specificity {
+ Specificity {
+ id_selectors: 0,
+ class_like_selectors: 0,
+ element_selectors: 0,
+ }
+ }
+}
+
+impl From<u32> for Specificity {
+ fn from(value: u32) -> Specificity {
+ assert!(value <= MAX_10BIT << 20 | MAX_10BIT << 10 | MAX_10BIT);
+ Specificity {
+ id_selectors: value >> 20,
+ class_like_selectors: (value >> 10) & MAX_10BIT,
+ element_selectors: value & MAX_10BIT,
+ }
+ }
+}
+
+impl From<Specificity> for u32 {
+ fn from(specificity: Specificity) -> u32 {
+ cmp::min(specificity.id_selectors, MAX_10BIT) << 20
+ | cmp::min(specificity.class_like_selectors, MAX_10BIT) << 10
+ | cmp::min(specificity.element_selectors, MAX_10BIT)
+ }
+}
+
+pub fn specificity<Impl>(iter: SelectorIter<Impl>) -> u32
+ where Impl: SelectorImpl
+{
+ complex_selector_specificity(iter).into()
+}
+
+fn complex_selector_specificity<Impl>(mut iter: SelectorIter<Impl>)
+ -> Specificity
+ where Impl: SelectorImpl
+{
+ fn simple_selector_specificity<Impl>(simple_selector: &Component<Impl>,
+ specificity: &mut Specificity)
+ where Impl: SelectorImpl
+ {
+ match *simple_selector {
+ Component::Combinator(..) => unreachable!(),
+ Component::PseudoElement(..) |
+ Component::LocalName(..) => {
+ specificity.element_selectors += 1
+ }
+ Component::ID(..) => {
+ specificity.id_selectors += 1
+ }
+ Component::Class(..) |
+ Component::AttributeInNoNamespace { .. } |
+ Component::AttributeInNoNamespaceExists { .. } |
+ Component::AttributeOther(..) |
+
+ Component::FirstChild | Component::LastChild |
+ Component::OnlyChild | Component::Root |
+ Component::Empty |
+ Component::NthChild(..) |
+ Component::NthLastChild(..) |
+ Component::NthOfType(..) |
+ Component::NthLastOfType(..) |
+ Component::FirstOfType | Component::LastOfType |
+ Component::OnlyOfType |
+ Component::NonTSPseudoClass(..) => {
+ specificity.class_like_selectors += 1
+ }
+ Component::ExplicitUniversalType |
+ Component::ExplicitAnyNamespace |
+ Component::ExplicitNoNamespace |
+ Component::DefaultNamespace(..) |
+ Component::Namespace(..) => {
+ // Does not affect specificity
+ }
+ Component::Negation(ref negated) => {
+ for ss in negated.iter() {
+ simple_selector_specificity(&ss, specificity);
+ }
+ }
+ }
+ }
+
+ let mut specificity = Default::default();
+ loop {
+ for simple_selector in &mut iter {
+ simple_selector_specificity(&simple_selector, &mut specificity);
+ }
+ if iter.next_sequence().is_none() {
+ break;
+ }
+ }
+ specificity
+}