1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/* 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 https://mozilla.org/MPL/2.0/. */

//! An invalidation processor for style changes due to document state changes.

use crate::dom::TElement;
use crate::invalidation::element::invalidation_map::Dependency;
use crate::invalidation::element::invalidator::{
    DescendantInvalidationLists, InvalidationVector, SiblingTraversalMap,
};
use crate::invalidation::element::invalidator::{Invalidation, InvalidationProcessor};
use crate::invalidation::element::state_and_attributes;
use crate::stylist::CascadeData;
use selectors::matching::{
    MatchingContext, MatchingForInvalidation, MatchingMode, NeedsSelectorFlags, QuirksMode,
    SelectorCaches, VisitedHandlingMode,
};
use style_traits::dom::DocumentState;

/// A struct holding the members necessary to invalidate document state
/// selectors.
#[derive(Debug)]
pub struct InvalidationMatchingData {
    /// The document state that has changed, which makes it always match.
    pub document_state: DocumentState,
}

impl Default for InvalidationMatchingData {
    #[inline(always)]
    fn default() -> Self {
        Self {
            document_state: DocumentState::empty(),
        }
    }
}

/// An invalidation processor for style changes due to state and attribute
/// changes.
pub struct DocumentStateInvalidationProcessor<'a, 'b, E: TElement, I> {
    rules: I,
    matching_context: MatchingContext<'a, E::Impl>,
    traversal_map: SiblingTraversalMap<E>,
    document_states_changed: DocumentState,
    _marker: std::marker::PhantomData<&'b ()>,
}

impl<'a, 'b, E: TElement, I> DocumentStateInvalidationProcessor<'a, 'b, E, I> {
    /// Creates a new DocumentStateInvalidationProcessor.
    #[inline]
    pub fn new(
        rules: I,
        document_states_changed: DocumentState,
        selector_caches: &'a mut SelectorCaches,
        quirks_mode: QuirksMode,
    ) -> Self {
        let mut matching_context = MatchingContext::<'a, E::Impl>::new_for_visited(
            MatchingMode::Normal,
            None,
            selector_caches,
            VisitedHandlingMode::AllLinksVisitedAndUnvisited,
            quirks_mode,
            NeedsSelectorFlags::No,
            MatchingForInvalidation::No,
        );

        matching_context.extra_data.invalidation_data.document_state = document_states_changed;

        Self {
            rules,
            document_states_changed,
            matching_context,
            traversal_map: SiblingTraversalMap::default(),
            _marker: std::marker::PhantomData,
        }
    }
}

impl<'a, 'b, E, I> InvalidationProcessor<'b, 'a, E>
    for DocumentStateInvalidationProcessor<'a, 'b, E, I>
where
    E: TElement,
    I: Iterator<Item = &'b CascadeData>,
{
    fn check_outer_dependency(&mut self, _: &Dependency, _: E) -> bool {
        debug_assert!(
            false,
            "how, we should only have parent-less dependencies here!"
        );
        true
    }

    fn collect_invalidations(
        &mut self,
        _element: E,
        self_invalidations: &mut InvalidationVector<'b>,
        _descendant_invalidations: &mut DescendantInvalidationLists<'b>,
        _sibling_invalidations: &mut InvalidationVector<'b>,
    ) -> bool {
        for cascade_data in &mut self.rules {
            let map = cascade_data.invalidation_map();
            for dependency in &map.document_state_selectors {
                if !dependency.state.intersects(self.document_states_changed) {
                    continue;
                }

                // We pass `None` as a scope, as document state selectors aren't
                // affected by the current scope.
                //
                // FIXME(emilio): We should really pass the relevant host for
                // self.rules, so that we invalidate correctly if the selector
                // happens to have something like :host(:-moz-window-inactive)
                // for example.
                self_invalidations.push(Invalidation::new(
                    &dependency.dependency,
                    /* scope = */ None,
                ));
            }
        }

        false
    }

    fn matching_context(&mut self) -> &mut MatchingContext<'a, E::Impl> {
        &mut self.matching_context
    }

    fn sibling_traversal_map(&self) -> &SiblingTraversalMap<E> {
        &self.traversal_map
    }

    fn recursion_limit_exceeded(&mut self, _: E) {
        unreachable!("We don't run document state invalidation with stack limits")
    }

    fn should_process_descendants(&mut self, element: E) -> bool {
        match element.borrow_data() {
            Some(d) => state_and_attributes::should_process_descendants(&d),
            None => false,
        }
    }

    fn invalidated_descendants(&mut self, element: E, child: E) {
        state_and_attributes::invalidated_descendants(element, child)
    }

    fn invalidated_self(&mut self, element: E) {
        state_and_attributes::invalidated_self(element);
    }

    fn invalidated_sibling(&mut self, sibling: E, of: E) {
        state_and_attributes::invalidated_sibling(sibling, of);
    }
}