1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 use attr
::{ParsedAttrSelectorOperation, AttrSelectorOperation, NamespaceConstraint}
;
6 use bloom
::BloomFilter
;
7 use parser
::{Combinator, ComplexSelector, Component, LocalName}
;
8 use parser
::{Selector, SelectorInner, SelectorIter}
;
9 use std
::borrow
::Borrow
;
12 // The bloom filter for descendant CSS selectors will have a <1% false
13 // positive rate until it has this many selectors in it, then it will
15 pub static RECOMMENDED_SELECTOR_BLOOM_FILTER_SIZE
: usize = 4096;
18 /// Set of flags that determine the different kind of elements affected by
19 /// the selector matching process.
21 /// This is used to implement efficient sharing.
23 pub flags StyleRelations
: usize {
24 /// Whether this element is affected by an ID selector.
25 const AFFECTED_BY_ID_SELECTOR
= 1 << 0,
26 /// Whether this element has a style attribute. Computed
28 const AFFECTED_BY_STYLE_ATTRIBUTE
= 1 << 1,
29 /// Whether this element is affected by presentational hints. This is
30 /// computed externally (that is, in Servo).
31 const AFFECTED_BY_PRESENTATIONAL_HINTS
= 1 << 2,
32 /// Whether this element has pseudo-element styles. Computed externally.
33 const AFFECTED_BY_PSEUDO_ELEMENTS
= 1 << 3,
38 /// Set of flags that are set on either the element or its parent (depending
39 /// on the flag) if the element could potentially match a selector.
40 pub flags ElementSelectorFlags
: usize {
41 /// When a child is added or removed from the parent, all the children
42 /// must be restyled, because they may match :nth-last-child,
43 /// :last-of-type, :nth-last-of-type, or :only-of-type.
44 const HAS_SLOW_SELECTOR
= 1 << 0,
46 /// When a child is added or removed from the parent, any later
47 /// children must be restyled, because they may match :nth-child,
48 /// :first-of-type, or :nth-of-type.
49 const HAS_SLOW_SELECTOR_LATER_SIBLINGS
= 1 << 1,
51 /// When a child is added or removed from the parent, the first and
52 /// last children must be restyled, because they may match :first-child,
53 /// :last-child, or :only-child.
54 const HAS_EDGE_CHILD_SELECTOR
= 1 << 2,
56 /// The element has an empty selector, so when a child is appended we
57 /// might need to restyle the parent completely.
58 const HAS_EMPTY_SELECTOR
= 1 << 3,
62 impl ElementSelectorFlags
{
63 /// Returns the subset of flags that apply to the element.
64 pub fn for_self(self) -> ElementSelectorFlags
{
65 self & (HAS_EMPTY_SELECTOR
)
68 /// Returns the subset of flags that apply to the parent.
69 pub fn for_parent(self) -> ElementSelectorFlags
{
70 self & (HAS_SLOW_SELECTOR
| HAS_SLOW_SELECTOR_LATER_SIBLINGS
| HAS_EDGE_CHILD_SELECTOR
)
74 /// What kind of selector matching mode we should use.
76 /// There are two modes of selector matching. The difference is only noticeable
77 /// in presence of pseudo-elements.
78 #[derive(Debug, PartialEq)]
79 pub enum MatchingMode
{
80 /// Don't ignore any pseudo-element selectors.
83 /// Ignores any stateless pseudo-element selectors in the rightmost sequence
84 /// of simple selectors.
86 /// This is useful, for example, to match against ::before when you aren't a
87 /// pseudo-element yourself.
89 /// For example, in presence of `::before:hover`, it would never match, but
90 /// `::before` would be ignored as in "matching".
92 /// It's required for all the selectors you match using this mode to have a
94 ForStatelessPseudoElement
,
98 /// Data associated with the matching process for a element. This context is
99 /// used across many selectors for an element, so it's not appropriate for
100 /// transient data that applies to only a single selector.
101 pub struct MatchingContext
<'a
> {
102 /// Output that records certains relations between elements noticed during
103 /// matching (and also extended after matching).
104 pub relations
: StyleRelations
,
105 /// The matching mode we should use when matching selectors.
106 pub matching_mode
: MatchingMode
,
107 /// The bloom filter used to fast-reject selectors.
108 pub bloom_filter
: Option
<&'a BloomFilter
>,
111 impl<'a
> MatchingContext
<'a
> {
112 /// Constructs a new `MatchingContext`.
113 pub fn new(matching_mode
: MatchingMode
,
114 bloom_filter
: Option
<&'a BloomFilter
>)
118 relations
: StyleRelations
::empty(),
119 matching_mode
: matching_mode
,
120 bloom_filter
: bloom_filter
,
125 pub fn matches_selector_list
<E
>(selector_list
: &[Selector
<E
::Impl
>],
127 context
: &mut MatchingContext
)
131 selector_list
.iter().any(|selector
| {
132 matches_selector(&selector
.inner
,
139 fn may_match
<E
>(sel
: &SelectorInner
<E
::Impl
>,
144 // Check against the list of precomputed hashes.
145 for hash
in sel
.ancestor_hashes
.iter() {
146 // If we hit the 0 sentinel hash, that means the rest are zero as well.
151 if !bf
.might_contain_hash(*hash
) {
159 /// A result of selector matching, includes 3 failure types,
161 /// NotMatchedAndRestartFromClosestLaterSibling
162 /// NotMatchedAndRestartFromClosestDescendant
163 /// NotMatchedGlobally
165 /// When NotMatchedGlobally appears, stop selector matching completely since
166 /// the succeeding selectors never matches.
167 /// It is raised when
168 /// Child combinator cannot find the candidate element.
169 /// Descendant combinator cannot find the candidate element.
171 /// When NotMatchedAndRestartFromClosestDescendant appears, the selector
172 /// matching does backtracking and restarts from the closest Descendant
174 /// It is raised when
175 /// NextSibling combinator cannot find the candidate element.
176 /// LaterSibling combinator cannot find the candidate element.
177 /// Child combinator doesn't match on the found element.
179 /// When NotMatchedAndRestartFromClosestLaterSibling appears, the selector
180 /// matching does backtracking and restarts from the closest LaterSibling
182 /// It is raised when
183 /// NextSibling combinator doesn't match on the found element.
185 /// For example, when the selector "d1 d2 a" is provided and we cannot *find*
186 /// an appropriate ancestor element for "d1", this selector matching raises
187 /// NotMatchedGlobally since even if "d2" is moved to more upper element, the
188 /// candidates for "d1" becomes less than before and d1 .
190 /// The next example is siblings. When the selector "b1 + b2 ~ d1 a" is
191 /// provided and we cannot *find* an appropriate brother element for b1,
192 /// the selector matching raises NotMatchedAndRestartFromClosestDescendant.
193 /// The selectors ("b1 + b2 ~") doesn't match and matching restart from "d1".
195 /// The additional example is child and sibling. When the selector
196 /// "b1 + c1 > b2 ~ d1 a" is provided and the selector "b1" doesn't match on
197 /// the element, this "b1" raises NotMatchedAndRestartFromClosestLaterSibling.
198 /// However since the selector "c1" raises
199 /// NotMatchedAndRestartFromClosestDescendant. So the selector
200 /// "b1 + c1 > b2 ~ " doesn't match and restart matching from "d1".
201 #[derive(PartialEq, Eq, Copy, Clone)]
202 enum SelectorMatchingResult
{
204 NotMatchedAndRestartFromClosestLaterSibling
,
205 NotMatchedAndRestartFromClosestDescendant
,
209 /// Matches an inner selector.
210 pub fn matches_selector
<E
, F
>(selector
: &SelectorInner
<E
::Impl
>,
212 context
: &mut MatchingContext
,
213 flags_setter
: &mut F
)
216 F
: FnMut(&E
, ElementSelectorFlags
),
218 // Use the bloom filter to fast-reject.
219 if let Some(filter
) = context
.bloom_filter
{
220 if !may_match
::<E
>(&selector
, filter
) {
225 matches_complex_selector(&selector
.complex
, element
, context
, flags_setter
)
228 /// Matches a complex selector.
230 /// Use `matches_selector` if you need to skip pseudos.
231 pub fn matches_complex_selector
<E
, F
>(complex_selector
: &ComplexSelector
<E
::Impl
>,
233 context
: &mut MatchingContext
,
234 flags_setter
: &mut F
)
237 F
: FnMut(&E
, ElementSelectorFlags
),
239 let mut iter
= complex_selector
.iter();
241 if cfg
!(debug_assertions
) {
242 if context
.matching_mode
== MatchingMode
::ForStatelessPseudoElement
{
243 assert
!(complex_selector
.iter().any(|c
| {
244 matches
!(*c
, Component
::PseudoElement(..))
249 if context
.matching_mode
== MatchingMode
::ForStatelessPseudoElement
{
250 match *iter
.next().unwrap() {
251 // Stateful pseudo, just don't match.
252 Component
::NonTSPseudoClass(..) => return false,
253 Component
::PseudoElement(..) => {
254 // Pseudo, just eat the whole sequence.
255 let next
= iter
.next();
256 debug_assert
!(next
.is_none(),
257 "Someone messed up pseudo-element parsing?");
259 if iter
.next_sequence().is_none() {
263 _
=> panic
!("Used MatchingMode::ForStatelessPseudoElement in a non-pseudo selector"),
267 match matches_complex_selector_internal(iter
,
271 SelectorMatchingResult
::Matched
=> true,
276 fn matches_complex_selector_internal
<E
, F
>(mut selector_iter
: SelectorIter
<E
::Impl
>,
278 context
: &mut MatchingContext
,
279 flags_setter
: &mut F
)
280 -> SelectorMatchingResult
282 F
: FnMut(&E
, ElementSelectorFlags
),
284 let matches_all_simple_selectors
= selector_iter
.all(|simple
| {
285 matches_simple_selector(simple
, element
, context
, flags_setter
)
288 let combinator
= selector_iter
.next_sequence();
289 let siblings
= combinator
.map_or(false, |c
| c
.is_sibling());
291 flags_setter(element
, HAS_SLOW_SELECTOR_LATER_SIBLINGS
);
294 if !matches_all_simple_selectors
{
295 return SelectorMatchingResult
::NotMatchedAndRestartFromClosestLaterSibling
;
299 None
=> SelectorMatchingResult
::Matched
,
301 let (mut next_element
, candidate_not_found
) = match c
{
302 Combinator
::NextSibling
| Combinator
::LaterSibling
=> {
303 (element
.prev_sibling_element(),
304 SelectorMatchingResult
::NotMatchedAndRestartFromClosestDescendant
)
306 Combinator
::Child
| Combinator
::Descendant
=> {
307 (element
.parent_element(),
308 SelectorMatchingResult
::NotMatchedGlobally
)
310 Combinator
::PseudoElement
=> {
311 (element
.pseudo_element_originating_element(),
312 SelectorMatchingResult
::NotMatchedGlobally
)
317 let element
= match next_element
{
318 None
=> return candidate_not_found
,
319 Some(next_element
) => next_element
,
321 let result
= matches_complex_selector_internal(selector_iter
.clone(),
326 // Return the status immediately.
327 (SelectorMatchingResult
::Matched
, _
) => return result
,
328 (SelectorMatchingResult
::NotMatchedGlobally
, _
) => return result
,
330 // Upgrade the failure status to
331 // NotMatchedAndRestartFromClosestDescendant.
332 (_
, Combinator
::PseudoElement
) |
333 (_
, Combinator
::Child
) => return SelectorMatchingResult
::NotMatchedAndRestartFromClosestDescendant
,
335 // Return the status directly.
336 (_
, Combinator
::NextSibling
) => return result
,
338 // If the failure status is NotMatchedAndRestartFromClosestDescendant
339 // and combinator is Combinator::LaterSibling, give up this Combinator::LaterSibling matching
340 // and restart from the closest descendant combinator.
341 (SelectorMatchingResult
::NotMatchedAndRestartFromClosestDescendant
, Combinator
::LaterSibling
)
344 // The Combinator::Descendant combinator and the status is
345 // NotMatchedAndRestartFromClosestLaterSibling or
346 // NotMatchedAndRestartFromClosestDescendant,
347 // or the Combinator::LaterSibling combinator and the status is
348 // NotMatchedAndRestartFromClosestDescendant
349 // can continue to matching on the next candidate element.
352 next_element
= if siblings
{
353 element
.prev_sibling_element()
355 element
.parent_element()
362 /// Determines whether the given element matches the given single selector.
364 fn matches_simple_selector
<E
, F
>(
365 selector
: &Component
<E
::Impl
>,
367 context
: &mut MatchingContext
,
368 flags_setter
: &mut F
)
371 F
: FnMut(&E
, ElementSelectorFlags
),
373 macro_rules
! relation_if
{
374 ($ex
:expr
, $flag
:ident
) => {
376 context
.relations
|= $flag
;
385 Component
::Combinator(_
) => unreachable
!(),
386 Component
::PseudoElement(ref pseudo
) => {
387 element
.match_pseudo_element(pseudo
, context
)
389 Component
::LocalName(LocalName { ref name, ref lower_name }
) => {
390 let is_html
= element
.is_html_element_in_html_document();
391 element
.get_local_name() == select_name(is_html
, name
, lower_name
).borrow()
393 Component
::ExplicitUniversalType
|
394 Component
::ExplicitAnyNamespace
=> {
397 Component
::Namespace(_
, ref url
) |
398 Component
::DefaultNamespace(ref url
) => {
399 element
.get_namespace() == url
.borrow()
401 Component
::ExplicitNoNamespace
=> {
402 let ns
= ::parser
::namespace_empty_string
::<E
::Impl
>();
403 element
.get_namespace() == ns
.borrow()
405 // TODO: case-sensitivity depends on the document type and quirks mode
406 Component
::ID(ref id
) => {
407 relation_if
!(element
.get_id().map_or(false, |attr
| attr
== *id
),
408 AFFECTED_BY_ID_SELECTOR
)
410 Component
::Class(ref class
) => {
411 element
.has_class(class
)
413 Component
::AttributeInNoNamespaceExists { ref local_name, ref local_name_lower }
=> {
414 let is_html
= element
.is_html_element_in_html_document();
415 element
.attr_matches(
416 &NamespaceConstraint
::Specific(&::parser
::namespace_empty_string
::<E
::Impl
>()),
417 select_name(is_html
, local_name
, local_name_lower
),
418 &AttrSelectorOperation
::Exists
421 Component
::AttributeInNoNamespace
{
423 ref local_name_lower
,
432 let is_html
= element
.is_html_element_in_html_document();
433 element
.attr_matches(
434 &NamespaceConstraint
::Specific(&::parser
::namespace_empty_string
::<E
::Impl
>()),
435 select_name(is_html
, local_name
, local_name_lower
),
436 &AttrSelectorOperation
::WithValue
{
438 case_sensitivity
: case_sensitivity
.to_unconditional(is_html
),
439 expected_value
: value
,
443 Component
::AttributeOther(ref attr_sel
) => {
444 if attr_sel
.never_matches
{
447 let is_html
= element
.is_html_element_in_html_document();
448 element
.attr_matches(
449 &attr_sel
.namespace(),
450 select_name(is_html
, &attr_sel
.local_name
, &attr_sel
.local_name_lower
),
451 &match attr_sel
.operation
{
452 ParsedAttrSelectorOperation
::Exists
=> AttrSelectorOperation
::Exists
,
453 ParsedAttrSelectorOperation
::WithValue
{
458 AttrSelectorOperation
::WithValue
{
460 case_sensitivity
: case_sensitivity
.to_unconditional(is_html
),
461 expected_value
: expected_value
,
467 Component
::NonTSPseudoClass(ref pc
) => {
468 element
.match_non_ts_pseudo_class(pc
, context
, flags_setter
)
470 Component
::FirstChild
=> {
471 matches_first_child(element
, flags_setter
)
473 Component
::LastChild
=> {
474 matches_last_child(element
, flags_setter
)
476 Component
::OnlyChild
=> {
477 matches_first_child(element
, flags_setter
) &&
478 matches_last_child(element
, flags_setter
)
481 // We never share styles with an element with no parent, so no point
482 // in creating a new StyleRelation.
485 Component
::Empty
=> {
486 flags_setter(element
, HAS_EMPTY_SELECTOR
);
489 Component
::NthChild(a
, b
) => {
490 matches_generic_nth_child(element
, a
, b
, false, false, flags_setter
)
492 Component
::NthLastChild(a
, b
) => {
493 matches_generic_nth_child(element
, a
, b
, false, true, flags_setter
)
495 Component
::NthOfType(a
, b
) => {
496 matches_generic_nth_child(element
, a
, b
, true, false, flags_setter
)
498 Component
::NthLastOfType(a
, b
) => {
499 matches_generic_nth_child(element
, a
, b
, true, true, flags_setter
)
501 Component
::FirstOfType
=> {
502 matches_generic_nth_child(element
, 0, 1, true, false, flags_setter
)
504 Component
::LastOfType
=> {
505 matches_generic_nth_child(element
, 0, 1, true, true, flags_setter
)
507 Component
::OnlyOfType
=> {
508 matches_generic_nth_child(element
, 0, 1, true, false, flags_setter
) &&
509 matches_generic_nth_child(element
, 0, 1, true, true, flags_setter
)
511 Component
::Negation(ref negated
) => {
512 !negated
.iter().all(|ss
| matches_simple_selector(ss
, element
, context
, flags_setter
))
517 fn select_name
<'a
, T
>(is_html
: bool
, local_name
: &'a T
, local_name_lower
: &'a T
) -> &'a T
{
526 fn matches_generic_nth_child
<E
, F
>(element
: &E
,
531 flags_setter
: &mut F
)
534 F
: FnMut(&E
, ElementSelectorFlags
),
536 flags_setter(element
, if is_from_end
{
539 HAS_SLOW_SELECTOR_LATER_SIBLINGS
542 let mut index
: i32 = 1;
543 let mut next_sibling
= if is_from_end
{
544 element
.next_sibling_element()
546 element
.prev_sibling_element()
550 let sibling
= match next_sibling
{
552 Some(next_sibling
) => next_sibling
556 if element
.get_local_name() == sibling
.get_local_name() &&
557 element
.get_namespace() == sibling
.get_namespace() {
563 next_sibling
= if is_from_end
{
564 sibling
.next_sibling_element()
566 sibling
.prev_sibling_element()
570 // Is there a non-negative integer n such that An+B=index?
571 match index
.checked_sub(b
) {
573 Some(an
) => match an
.checked_div(a
) {
574 Some(n
) => n
>= 0 && a
* n
== an
,
575 None
/* a == 0 */ => an
== 0,
581 fn matches_first_child
<E
, F
>(element
: &E
, flags_setter
: &mut F
) -> bool
583 F
: FnMut(&E
, ElementSelectorFlags
),
585 flags_setter(element
, HAS_EDGE_CHILD_SELECTOR
);
586 element
.prev_sibling_element().is_none()
590 fn matches_last_child
<E
, F
>(element
: &E
, flags_setter
: &mut F
) -> bool
592 F
: FnMut(&E
, ElementSelectorFlags
),
594 flags_setter(element
, HAS_EDGE_CHILD_SELECTOR
);
595 element
.next_sibling_element().is_none()