1 | /* |
2 | * Copyright (C) 2012, 2014, 2017 Apple Inc. All rights reserved. |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without |
5 | * modification, are permitted provided that the following conditions |
6 | * are met: |
7 | * 1. Redistributions of source code must retain the above copyright |
8 | * notice, this list of conditions and the following disclaimer. |
9 | * 2. Redistributions in binary form must reproduce the above copyright |
10 | * notice, this list of conditions and the following disclaimer in the |
11 | * documentation and/or other materials provided with the distribution. |
12 | * |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
24 | */ |
25 | |
26 | #include "config.h" |
27 | #include "StyleInvalidator.h" |
28 | |
29 | #include "CSSSelectorList.h" |
30 | #include "Document.h" |
31 | #include "ElementIterator.h" |
32 | #include "ElementRuleCollector.h" |
33 | #include "HTMLSlotElement.h" |
34 | #include "SelectorFilter.h" |
35 | #include "ShadowRoot.h" |
36 | #include "StyleRuleImport.h" |
37 | #include "StyleSheetContents.h" |
38 | |
39 | namespace WebCore { |
40 | namespace Style { |
41 | |
42 | static bool shouldDirtyAllStyle(const Vector<RefPtr<StyleRuleBase>>& rules) |
43 | { |
44 | for (auto& rule : rules) { |
45 | if (is<StyleRuleMedia>(*rule)) { |
46 | const auto* childRules = downcast<StyleRuleMedia>(*rule).childRulesWithoutDeferredParsing(); |
47 | if (childRules && shouldDirtyAllStyle(*childRules)) |
48 | return true; |
49 | continue; |
50 | } |
51 | // FIXME: At least font faces don't need full recalc in all cases. |
52 | if (!is<StyleRule>(*rule)) |
53 | return true; |
54 | } |
55 | return false; |
56 | } |
57 | |
58 | static bool shouldDirtyAllStyle(const StyleSheetContents& sheet) |
59 | { |
60 | for (auto& import : sheet.importRules()) { |
61 | if (!import->styleSheet()) |
62 | continue; |
63 | if (shouldDirtyAllStyle(*import->styleSheet())) |
64 | return true; |
65 | } |
66 | if (shouldDirtyAllStyle(sheet.childRules())) |
67 | return true; |
68 | return false; |
69 | } |
70 | |
71 | static bool shouldDirtyAllStyle(const Vector<StyleSheetContents*>& sheets) |
72 | { |
73 | for (auto& sheet : sheets) { |
74 | if (shouldDirtyAllStyle(*sheet)) |
75 | return true; |
76 | } |
77 | return false; |
78 | } |
79 | |
80 | Invalidator::Invalidator(const Vector<StyleSheetContents*>& sheets, const MediaQueryEvaluator& mediaQueryEvaluator) |
81 | : m_ownedRuleSet(std::make_unique<RuleSet>()) |
82 | , m_ruleSet(*m_ownedRuleSet) |
83 | , m_dirtiesAllStyle(shouldDirtyAllStyle(sheets)) |
84 | { |
85 | if (m_dirtiesAllStyle) |
86 | return; |
87 | |
88 | m_ownedRuleSet->disableAutoShrinkToFit(); |
89 | for (auto& sheet : sheets) |
90 | m_ownedRuleSet->addRulesFromSheet(*sheet, mediaQueryEvaluator); |
91 | |
92 | m_hasShadowPseudoElementRulesInAuthorSheet = m_ruleSet.hasShadowPseudoElementRules(); |
93 | } |
94 | |
95 | Invalidator::Invalidator(const RuleSet& ruleSet) |
96 | : m_ruleSet(ruleSet) |
97 | , m_hasShadowPseudoElementRulesInAuthorSheet(ruleSet.hasShadowPseudoElementRules()) |
98 | { |
99 | } |
100 | |
101 | Invalidator::CheckDescendants Invalidator::invalidateIfNeeded(Element& element, const SelectorFilter* filter) |
102 | { |
103 | if (m_hasShadowPseudoElementRulesInAuthorSheet) { |
104 | // FIXME: This could do actual rule matching too. |
105 | if (element.shadowRoot()) |
106 | element.invalidateStyleForSubtreeInternal(); |
107 | } |
108 | |
109 | bool shouldCheckForSlots = !m_ruleSet.slottedPseudoElementRules().isEmpty() && !m_didInvalidateHostChildren; |
110 | if (shouldCheckForSlots && is<HTMLSlotElement>(element)) { |
111 | auto* containingShadowRoot = element.containingShadowRoot(); |
112 | if (containingShadowRoot && containingShadowRoot->host()) { |
113 | for (auto& possiblySlotted : childrenOfType<Element>(*containingShadowRoot->host())) |
114 | possiblySlotted.invalidateStyleInternal(); |
115 | } |
116 | // No need to do this again. |
117 | m_didInvalidateHostChildren = true; |
118 | } |
119 | |
120 | switch (element.styleValidity()) { |
121 | case Style::Validity::Valid: { |
122 | ElementRuleCollector ruleCollector(element, m_ruleSet, filter); |
123 | ruleCollector.setMode(SelectorChecker::Mode::CollectingRulesIgnoringVirtualPseudoElements); |
124 | ruleCollector.matchAuthorRules(false); |
125 | |
126 | if (ruleCollector.hasMatchedRules()) |
127 | element.invalidateStyleInternal(); |
128 | return CheckDescendants::Yes; |
129 | } |
130 | case Style::Validity::ElementInvalid: |
131 | return CheckDescendants::Yes; |
132 | case Style::Validity::SubtreeInvalid: |
133 | case Style::Validity::SubtreeAndRenderersInvalid: |
134 | if (shouldCheckForSlots) |
135 | return CheckDescendants::Yes; |
136 | return CheckDescendants::No; |
137 | } |
138 | ASSERT_NOT_REACHED(); |
139 | return CheckDescendants::Yes; |
140 | } |
141 | |
142 | void Invalidator::invalidateStyleForTree(Element& root, SelectorFilter* filter) |
143 | { |
144 | if (invalidateIfNeeded(root, filter) == CheckDescendants::No) |
145 | return; |
146 | invalidateStyleForDescendants(root, filter); |
147 | } |
148 | |
149 | void Invalidator::invalidateStyleForDescendants(Element& root, SelectorFilter* filter) |
150 | { |
151 | Vector<Element*, 20> parentStack; |
152 | Element* previousElement = &root; |
153 | auto descendants = descendantsOfType<Element>(root); |
154 | for (auto it = descendants.begin(), end = descendants.end(); it != end;) { |
155 | auto& descendant = *it; |
156 | auto* parent = descendant.parentElement(); |
157 | if (parentStack.isEmpty() || parentStack.last() != parent) { |
158 | if (parent == previousElement) { |
159 | parentStack.append(parent); |
160 | if (filter) |
161 | filter->pushParentInitializingIfNeeded(*parent); |
162 | } else { |
163 | while (parentStack.last() != parent) { |
164 | parentStack.removeLast(); |
165 | if (filter) |
166 | filter->popParent(); |
167 | } |
168 | } |
169 | } |
170 | previousElement = &descendant; |
171 | |
172 | if (invalidateIfNeeded(descendant, filter) == CheckDescendants::Yes) |
173 | it.traverseNext(); |
174 | else |
175 | it.traverseNextSkippingChildren(); |
176 | } |
177 | } |
178 | |
179 | void Invalidator::invalidateStyle(Document& document) |
180 | { |
181 | ASSERT(!m_dirtiesAllStyle); |
182 | |
183 | Element* documentElement = document.documentElement(); |
184 | if (!documentElement) |
185 | return; |
186 | |
187 | SelectorFilter filter; |
188 | invalidateStyleForTree(*documentElement, &filter); |
189 | } |
190 | |
191 | void Invalidator::invalidateStyle(ShadowRoot& shadowRoot) |
192 | { |
193 | ASSERT(!m_dirtiesAllStyle); |
194 | |
195 | if (!m_ruleSet.hostPseudoClassRules().isEmpty() && shadowRoot.host()) |
196 | shadowRoot.host()->invalidateStyleInternal(); |
197 | |
198 | for (auto& child : childrenOfType<Element>(shadowRoot)) { |
199 | SelectorFilter filter; |
200 | invalidateStyleForTree(child, &filter); |
201 | } |
202 | } |
203 | |
204 | void Invalidator::invalidateStyle(Element& element) |
205 | { |
206 | ASSERT(!m_dirtiesAllStyle); |
207 | |
208 | // Don't use SelectorFilter as the rule sets here tend to be small and the filter would have setup cost deep in the tree. |
209 | invalidateStyleForTree(element, nullptr); |
210 | } |
211 | |
212 | void Invalidator::invalidateStyleWithMatchElement(Element& element, MatchElement matchElement) |
213 | { |
214 | switch (matchElement) { |
215 | case MatchElement::Subject: { |
216 | invalidateIfNeeded(element, nullptr); |
217 | break; |
218 | } |
219 | case MatchElement::Parent: { |
220 | auto children = childrenOfType<Element>(element); |
221 | for (auto& child : children) |
222 | invalidateIfNeeded(child, nullptr); |
223 | break; |
224 | } |
225 | case MatchElement::Ancestor: { |
226 | SelectorFilter filter; |
227 | invalidateStyleForDescendants(element, &filter); |
228 | break; |
229 | } |
230 | case MatchElement::DirectSibling: |
231 | if (auto* sibling = element.nextElementSibling()) |
232 | invalidateIfNeeded(*sibling, nullptr); |
233 | break; |
234 | case MatchElement::IndirectSibling: |
235 | for (auto* sibling = element.nextElementSibling(); sibling; sibling = sibling->nextElementSibling()) |
236 | invalidateIfNeeded(*sibling, nullptr); |
237 | break; |
238 | case MatchElement::AnySibling: { |
239 | auto parentChildren = childrenOfType<Element>(*element.parentNode()); |
240 | for (auto& parentChild : parentChildren) |
241 | invalidateIfNeeded(parentChild, nullptr); |
242 | break; |
243 | } |
244 | case MatchElement::ParentSibling: |
245 | for (auto* sibling = element.nextElementSibling(); sibling; sibling = sibling->nextElementSibling()) { |
246 | auto siblingChildren = childrenOfType<Element>(*sibling); |
247 | for (auto& siblingChild : siblingChildren) |
248 | invalidateIfNeeded(siblingChild, nullptr); |
249 | } |
250 | break; |
251 | case MatchElement::AncestorSibling: { |
252 | SelectorFilter filter; |
253 | for (auto* sibling = element.nextElementSibling(); sibling; sibling = sibling->nextElementSibling()) { |
254 | filter.popParentsUntil(element.parentElement()); |
255 | invalidateStyleForDescendants(*sibling, &filter); |
256 | } |
257 | break; |
258 | } |
259 | case MatchElement::Host: |
260 | // FIXME: Handle this here as well. |
261 | break; |
262 | } |
263 | } |
264 | |
265 | } |
266 | } |
267 | |