1 | /* |
2 | * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
3 | * (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com) |
4 | * Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com) |
5 | * Copyright (C) 2005-2018 Apple Inc. All rights reserved. |
6 | * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org> |
7 | * Copyright (C) 2007, 2008 Eric Seidel <eric@webkit.org> |
8 | * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) |
9 | * Copyright (c) 2011, Code Aurora Forum. All rights reserved. |
10 | * Copyright (C) Research In Motion Limited 2011. All rights reserved. |
11 | * Copyright (C) 2012 Google Inc. All rights reserved. |
12 | * |
13 | * This library is free software; you can redistribute it and/or |
14 | * modify it under the terms of the GNU Library General Public |
15 | * License as published by the Free Software Foundation; either |
16 | * version 2 of the License, or (at your option) any later version. |
17 | * |
18 | * This library is distributed in the hope that it will be useful, |
19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
21 | * Library General Public License for more details. |
22 | * |
23 | * You should have received a copy of the GNU Library General Public License |
24 | * along with this library; see the file COPYING.LIB. If not, write to |
25 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
26 | * Boston, MA 02110-1301, USA. |
27 | */ |
28 | |
29 | #include "config.h" |
30 | #include "ElementRuleCollector.h" |
31 | |
32 | #include "CSSDefaultStyleSheets.h" |
33 | #include "CSSRuleList.h" |
34 | #include "CSSSelector.h" |
35 | #include "CSSValueKeywords.h" |
36 | #include "HTMLElement.h" |
37 | #include "HTMLSlotElement.h" |
38 | #include "SVGElement.h" |
39 | #include "SelectorCompiler.h" |
40 | #include "SelectorFilter.h" |
41 | #include "ShadowRoot.h" |
42 | #include "StyleProperties.h" |
43 | #include "StyleScope.h" |
44 | #include "StyledElement.h" |
45 | #include <wtf/SetForScope.h> |
46 | |
47 | namespace WebCore { |
48 | |
49 | static const StyleProperties& leftToRightDeclaration() |
50 | { |
51 | static auto& declaration = [] () -> const StyleProperties& { |
52 | auto properties = MutableStyleProperties::create(); |
53 | properties->setProperty(CSSPropertyDirection, CSSValueLtr); |
54 | return properties.leakRef(); |
55 | }(); |
56 | return declaration; |
57 | } |
58 | |
59 | static const StyleProperties& rightToLeftDeclaration() |
60 | { |
61 | static auto& declaration = [] () -> const StyleProperties& { |
62 | auto properties = MutableStyleProperties::create(); |
63 | properties->setProperty(CSSPropertyDirection, CSSValueRtl); |
64 | return properties.leakRef(); |
65 | }(); |
66 | return declaration; |
67 | } |
68 | |
69 | class MatchRequest { |
70 | public: |
71 | MatchRequest(const RuleSet* ruleSet, bool includeEmptyRules = false, Style::ScopeOrdinal styleScopeOrdinal = Style::ScopeOrdinal::Element) |
72 | : ruleSet(ruleSet) |
73 | , includeEmptyRules(includeEmptyRules) |
74 | , styleScopeOrdinal(styleScopeOrdinal) |
75 | { |
76 | } |
77 | const RuleSet* ruleSet; |
78 | const bool includeEmptyRules; |
79 | Style::ScopeOrdinal styleScopeOrdinal; |
80 | }; |
81 | |
82 | ElementRuleCollector::ElementRuleCollector(const Element& element, const DocumentRuleSets& ruleSets, const SelectorFilter* selectorFilter) |
83 | : m_element(element) |
84 | , m_authorStyle(ruleSets.authorStyle()) |
85 | , m_userStyle(ruleSets.userStyle()) |
86 | , m_userAgentMediaQueryStyle(ruleSets.userAgentMediaQueryStyle()) |
87 | , m_selectorFilter(selectorFilter) |
88 | { |
89 | ASSERT(!m_selectorFilter || m_selectorFilter->parentStackIsConsistent(element.parentNode())); |
90 | } |
91 | |
92 | ElementRuleCollector::ElementRuleCollector(const Element& element, const RuleSet& authorStyle, const SelectorFilter* selectorFilter) |
93 | : m_element(element) |
94 | , m_authorStyle(authorStyle) |
95 | , m_selectorFilter(selectorFilter) |
96 | { |
97 | ASSERT(!m_selectorFilter || m_selectorFilter->parentStackIsConsistent(element.parentNode())); |
98 | } |
99 | |
100 | StyleResolver::MatchResult& ElementRuleCollector::matchedResult() |
101 | { |
102 | ASSERT(m_mode == SelectorChecker::Mode::ResolvingStyle); |
103 | return m_result; |
104 | } |
105 | |
106 | const Vector<RefPtr<StyleRule>>& ElementRuleCollector::matchedRuleList() const |
107 | { |
108 | ASSERT(m_mode == SelectorChecker::Mode::CollectingRules); |
109 | return m_matchedRuleList; |
110 | } |
111 | |
112 | inline void ElementRuleCollector::addMatchedRule(const RuleData& ruleData, unsigned specificity, Style::ScopeOrdinal styleScopeOrdinal, StyleResolver::RuleRange& ruleRange) |
113 | { |
114 | // Update our first/last rule indices in the matched rules array. |
115 | ++ruleRange.lastRuleIndex; |
116 | if (ruleRange.firstRuleIndex == -1) |
117 | ruleRange.firstRuleIndex = ruleRange.lastRuleIndex; |
118 | |
119 | m_matchedRules.append({ &ruleData, specificity, styleScopeOrdinal }); |
120 | } |
121 | |
122 | void ElementRuleCollector::clearMatchedRules() |
123 | { |
124 | m_matchedRules.clear(); |
125 | m_keepAliveSlottedPseudoElementRules.clear(); |
126 | } |
127 | |
128 | inline void ElementRuleCollector::addElementStyleProperties(const StyleProperties* propertySet, bool isCacheable) |
129 | { |
130 | if (!propertySet) |
131 | return; |
132 | m_result.ranges.lastAuthorRule = m_result.matchedProperties().size(); |
133 | if (m_result.ranges.firstAuthorRule == -1) |
134 | m_result.ranges.firstAuthorRule = m_result.ranges.lastAuthorRule; |
135 | m_result.addMatchedProperties(*propertySet); |
136 | if (!isCacheable) |
137 | m_result.isCacheable = false; |
138 | } |
139 | |
140 | void ElementRuleCollector::collectMatchingRules(const MatchRequest& matchRequest, StyleResolver::RuleRange& ruleRange) |
141 | { |
142 | ASSERT(matchRequest.ruleSet); |
143 | ASSERT_WITH_MESSAGE(!(m_mode == SelectorChecker::Mode::CollectingRulesIgnoringVirtualPseudoElements && m_pseudoStyleRequest.pseudoId != PseudoId::None), "When in StyleInvalidation or SharingRules, SelectorChecker does not try to match the pseudo ID. While ElementRuleCollector supports matching a particular pseudoId in this case, this would indicate a error at the call site since matching a particular element should be unnecessary." ); |
144 | |
145 | auto* shadowRoot = m_element.containingShadowRoot(); |
146 | if (shadowRoot && shadowRoot->mode() == ShadowRootMode::UserAgent) |
147 | collectMatchingShadowPseudoElementRules(matchRequest, ruleRange); |
148 | |
149 | // We need to collect the rules for id, class, tag, and everything else into a buffer and |
150 | // then sort the buffer. |
151 | auto& id = m_element.idForStyleResolution(); |
152 | if (!id.isNull()) |
153 | collectMatchingRulesForList(matchRequest.ruleSet->idRules(id), matchRequest, ruleRange); |
154 | if (m_element.hasClass()) { |
155 | for (size_t i = 0; i < m_element.classNames().size(); ++i) |
156 | collectMatchingRulesForList(matchRequest.ruleSet->classRules(m_element.classNames()[i]), matchRequest, ruleRange); |
157 | } |
158 | |
159 | if (m_element.isLink()) |
160 | collectMatchingRulesForList(matchRequest.ruleSet->linkPseudoClassRules(), matchRequest, ruleRange); |
161 | if (SelectorChecker::matchesFocusPseudoClass(m_element)) |
162 | collectMatchingRulesForList(matchRequest.ruleSet->focusPseudoClassRules(), matchRequest, ruleRange); |
163 | collectMatchingRulesForList(matchRequest.ruleSet->tagRules(m_element.localName(), m_element.isHTMLElement() && m_element.document().isHTMLDocument()), matchRequest, ruleRange); |
164 | collectMatchingRulesForList(matchRequest.ruleSet->universalRules(), matchRequest, ruleRange); |
165 | } |
166 | |
167 | void ElementRuleCollector::sortAndTransferMatchedRules() |
168 | { |
169 | if (m_matchedRules.isEmpty()) |
170 | return; |
171 | |
172 | sortMatchedRules(); |
173 | |
174 | if (m_mode == SelectorChecker::Mode::CollectingRules) { |
175 | for (const MatchedRule& matchedRule : m_matchedRules) |
176 | m_matchedRuleList.append(matchedRule.ruleData->rule()); |
177 | return; |
178 | } |
179 | |
180 | for (const MatchedRule& matchedRule : m_matchedRules) { |
181 | m_result.addMatchedProperties(matchedRule.ruleData->rule()->properties(), matchedRule.ruleData->rule(), matchedRule.ruleData->linkMatchType(), matchedRule.ruleData->propertyWhitelistType(), matchedRule.styleScopeOrdinal); |
182 | } |
183 | } |
184 | |
185 | void ElementRuleCollector::matchAuthorRules(bool includeEmptyRules) |
186 | { |
187 | clearMatchedRules(); |
188 | |
189 | m_result.ranges.lastAuthorRule = m_result.matchedProperties().size() - 1; |
190 | StyleResolver::RuleRange ruleRange = m_result.ranges.authorRuleRange(); |
191 | |
192 | { |
193 | MatchRequest matchRequest(&m_authorStyle, includeEmptyRules); |
194 | collectMatchingRules(matchRequest, ruleRange); |
195 | } |
196 | |
197 | auto* parent = m_element.parentElement(); |
198 | if (parent && parent->shadowRoot()) |
199 | matchSlottedPseudoElementRules(includeEmptyRules, ruleRange); |
200 | |
201 | if (m_element.shadowRoot()) |
202 | matchHostPseudoClassRules(includeEmptyRules, ruleRange); |
203 | |
204 | if (m_element.isInShadowTree()) |
205 | matchAuthorShadowPseudoElementRules(includeEmptyRules, ruleRange); |
206 | |
207 | sortAndTransferMatchedRules(); |
208 | } |
209 | |
210 | void ElementRuleCollector::matchAuthorShadowPseudoElementRules(bool includeEmptyRules, StyleResolver::RuleRange& ruleRange) |
211 | { |
212 | ASSERT(m_element.isInShadowTree()); |
213 | auto& shadowRoot = *m_element.containingShadowRoot(); |
214 | if (shadowRoot.mode() != ShadowRootMode::UserAgent) |
215 | return; |
216 | // Look up shadow pseudo elements also from the host scope author style as they are web-exposed. |
217 | auto& hostAuthorRules = Style::Scope::forNode(*shadowRoot.host()).resolver().ruleSets().authorStyle(); |
218 | MatchRequest hostAuthorRequest { &hostAuthorRules, includeEmptyRules, Style::ScopeOrdinal::ContainingHost }; |
219 | collectMatchingShadowPseudoElementRules(hostAuthorRequest, ruleRange); |
220 | } |
221 | |
222 | void ElementRuleCollector::matchHostPseudoClassRules(bool includeEmptyRules, StyleResolver::RuleRange& ruleRange) |
223 | { |
224 | ASSERT(m_element.shadowRoot()); |
225 | |
226 | auto& shadowAuthorStyle = m_element.shadowRoot()->styleScope().resolver().ruleSets().authorStyle(); |
227 | auto& shadowHostRules = shadowAuthorStyle.hostPseudoClassRules(); |
228 | if (shadowHostRules.isEmpty()) |
229 | return; |
230 | |
231 | SetForScope<bool> change(m_isMatchingHostPseudoClass, true); |
232 | |
233 | MatchRequest hostMatchRequest { nullptr, includeEmptyRules, Style::ScopeOrdinal::Shadow }; |
234 | collectMatchingRulesForList(&shadowHostRules, hostMatchRequest, ruleRange); |
235 | } |
236 | |
237 | void ElementRuleCollector::matchSlottedPseudoElementRules(bool includeEmptyRules, StyleResolver::RuleRange& ruleRange) |
238 | { |
239 | auto* slot = m_element.assignedSlot(); |
240 | auto styleScopeOrdinal = Style::ScopeOrdinal::FirstSlot; |
241 | |
242 | for (; slot; slot = slot->assignedSlot(), ++styleScopeOrdinal) { |
243 | auto& styleScope = Style::Scope::forNode(*slot); |
244 | if (!styleScope.resolver().ruleSets().isAuthorStyleDefined()) |
245 | continue; |
246 | // Find out if there are any ::slotted rules in the shadow tree matching the current slot. |
247 | // FIXME: This is really part of the slot style and could be cached when resolving it. |
248 | ElementRuleCollector collector(*slot, styleScope.resolver().ruleSets().authorStyle(), nullptr); |
249 | auto slottedPseudoElementRules = collector.collectSlottedPseudoElementRulesForSlot(includeEmptyRules); |
250 | if (!slottedPseudoElementRules) |
251 | continue; |
252 | // Match in the current scope. |
253 | SetForScope<bool> change(m_isMatchingSlottedPseudoElements, true); |
254 | |
255 | MatchRequest scopeMatchRequest(nullptr, includeEmptyRules, styleScopeOrdinal); |
256 | collectMatchingRulesForList(slottedPseudoElementRules.get(), scopeMatchRequest, ruleRange); |
257 | |
258 | m_keepAliveSlottedPseudoElementRules.append(WTFMove(slottedPseudoElementRules)); |
259 | } |
260 | } |
261 | |
262 | void ElementRuleCollector::collectMatchingShadowPseudoElementRules(const MatchRequest& matchRequest, StyleResolver::RuleRange& ruleRange) |
263 | { |
264 | ASSERT(matchRequest.ruleSet); |
265 | ASSERT(m_element.containingShadowRoot()->mode() == ShadowRootMode::UserAgent); |
266 | |
267 | auto& rules = *matchRequest.ruleSet; |
268 | #if ENABLE(VIDEO_TRACK) |
269 | // FXIME: WebVTT should not be done by styling UA shadow trees like this. |
270 | if (m_element.isWebVTTElement()) |
271 | collectMatchingRulesForList(rules.cuePseudoRules(), matchRequest, ruleRange); |
272 | #endif |
273 | auto& pseudoId = m_element.shadowPseudoId(); |
274 | if (!pseudoId.isEmpty()) |
275 | collectMatchingRulesForList(rules.shadowPseudoElementRules(pseudoId), matchRequest, ruleRange); |
276 | } |
277 | |
278 | std::unique_ptr<RuleSet::RuleDataVector> ElementRuleCollector::collectSlottedPseudoElementRulesForSlot(bool includeEmptyRules) |
279 | { |
280 | ASSERT(is<HTMLSlotElement>(m_element)); |
281 | |
282 | clearMatchedRules(); |
283 | |
284 | m_mode = SelectorChecker::Mode::CollectingRules; |
285 | |
286 | // Match global author rules. |
287 | MatchRequest matchRequest(&m_authorStyle, includeEmptyRules); |
288 | StyleResolver::RuleRange ruleRange = m_result.ranges.authorRuleRange(); |
289 | collectMatchingRulesForList(&m_authorStyle.slottedPseudoElementRules(), matchRequest, ruleRange); |
290 | |
291 | if (m_matchedRules.isEmpty()) |
292 | return { }; |
293 | |
294 | auto ruleDataVector = std::make_unique<RuleSet::RuleDataVector>(); |
295 | ruleDataVector->reserveInitialCapacity(m_matchedRules.size()); |
296 | for (auto& matchedRule : m_matchedRules) |
297 | ruleDataVector->uncheckedAppend(*matchedRule.ruleData); |
298 | |
299 | return ruleDataVector; |
300 | } |
301 | |
302 | void ElementRuleCollector::matchUserRules(bool includeEmptyRules) |
303 | { |
304 | if (!m_userStyle) |
305 | return; |
306 | |
307 | clearMatchedRules(); |
308 | |
309 | m_result.ranges.lastUserRule = m_result.matchedProperties().size() - 1; |
310 | MatchRequest matchRequest(m_userStyle, includeEmptyRules); |
311 | StyleResolver::RuleRange ruleRange = m_result.ranges.userRuleRange(); |
312 | collectMatchingRules(matchRequest, ruleRange); |
313 | |
314 | sortAndTransferMatchedRules(); |
315 | } |
316 | |
317 | void ElementRuleCollector::matchUARules() |
318 | { |
319 | // First we match rules from the user agent sheet. |
320 | if (CSSDefaultStyleSheets::simpleDefaultStyleSheet) |
321 | m_result.isCacheable = false; |
322 | RuleSet* userAgentStyleSheet = m_isPrintStyle |
323 | ? CSSDefaultStyleSheets::defaultPrintStyle : CSSDefaultStyleSheets::defaultStyle; |
324 | matchUARules(*userAgentStyleSheet); |
325 | |
326 | // In quirks mode, we match rules from the quirks user agent sheet. |
327 | if (m_element.document().inQuirksMode()) |
328 | matchUARules(*CSSDefaultStyleSheets::defaultQuirksStyle); |
329 | |
330 | if (m_userAgentMediaQueryStyle) |
331 | matchUARules(*m_userAgentMediaQueryStyle); |
332 | } |
333 | |
334 | void ElementRuleCollector::matchUARules(const RuleSet& rules) |
335 | { |
336 | clearMatchedRules(); |
337 | |
338 | m_result.ranges.lastUARule = m_result.matchedProperties().size() - 1; |
339 | StyleResolver::RuleRange ruleRange = m_result.ranges.UARuleRange(); |
340 | collectMatchingRules(MatchRequest(&rules), ruleRange); |
341 | |
342 | sortAndTransferMatchedRules(); |
343 | } |
344 | |
345 | static const CSSSelector* findSlottedPseudoElementSelector(const CSSSelector* selector) |
346 | { |
347 | for (; selector; selector = selector->tagHistory()) { |
348 | if (selector->match() == CSSSelector::PseudoElement && selector->pseudoElementType() == CSSSelector::PseudoElementSlotted) { |
349 | if (auto* list = selector->selectorList()) |
350 | return list->first(); |
351 | break; |
352 | } |
353 | }; |
354 | return nullptr; |
355 | } |
356 | |
357 | inline bool ElementRuleCollector::ruleMatches(const RuleData& ruleData, unsigned& specificity) |
358 | { |
359 | // We know a sufficiently simple single part selector matches simply because we found it from the rule hash when filtering the RuleSet. |
360 | // This is limited to HTML only so we don't need to check the namespace (because of tag name match). |
361 | MatchBasedOnRuleHash matchBasedOnRuleHash = ruleData.matchBasedOnRuleHash(); |
362 | if (matchBasedOnRuleHash != MatchBasedOnRuleHash::None && m_element.isHTMLElement()) { |
363 | ASSERT_WITH_MESSAGE(m_pseudoStyleRequest.pseudoId == PseudoId::None, "If we match based on the rule hash while collecting for a particular pseudo element ID, we would add incorrect rules for that pseudo element ID. We should never end in ruleMatches() with a pseudo element if the ruleData cannot match any pseudo element." ); |
364 | |
365 | switch (matchBasedOnRuleHash) { |
366 | case MatchBasedOnRuleHash::None: |
367 | ASSERT_NOT_REACHED(); |
368 | break; |
369 | case MatchBasedOnRuleHash::Universal: |
370 | specificity = 0; |
371 | break; |
372 | case MatchBasedOnRuleHash::ClassA: |
373 | specificity = static_cast<unsigned>(SelectorSpecificityIncrement::ClassA); |
374 | break; |
375 | case MatchBasedOnRuleHash::ClassB: |
376 | specificity = static_cast<unsigned>(SelectorSpecificityIncrement::ClassB); |
377 | break; |
378 | case MatchBasedOnRuleHash::ClassC: |
379 | specificity = static_cast<unsigned>(SelectorSpecificityIncrement::ClassC); |
380 | break; |
381 | } |
382 | return true; |
383 | } |
384 | |
385 | #if ENABLE(CSS_SELECTOR_JIT) |
386 | auto& compiledSelector = ruleData.rule()->compiledSelectorForListIndex(ruleData.selectorListIndex()); |
387 | void* compiledSelectorChecker = compiledSelector.codeRef.code().executableAddress(); |
388 | if (!compiledSelectorChecker && compiledSelector.status == SelectorCompilationStatus::NotCompiled) { |
389 | compiledSelector.status = SelectorCompiler::compileSelector(ruleData.selector(), SelectorCompiler::SelectorContext::RuleCollector, compiledSelector.codeRef); |
390 | |
391 | compiledSelectorChecker = compiledSelector.codeRef.code().executableAddress(); |
392 | } |
393 | |
394 | if (compiledSelectorChecker && compiledSelector.status == SelectorCompilationStatus::SimpleSelectorChecker) { |
395 | auto selectorChecker = SelectorCompiler::ruleCollectorSimpleSelectorCheckerFunction(compiledSelectorChecker, compiledSelector.status); |
396 | #if !ASSERT_MSG_DISABLED |
397 | unsigned ignoreSpecificity; |
398 | ASSERT_WITH_MESSAGE(!selectorChecker(&m_element, &ignoreSpecificity) || m_pseudoStyleRequest.pseudoId == PseudoId::None, "When matching pseudo elements, we should never compile a selector checker without context unless it cannot match anything." ); |
399 | #endif |
400 | #if CSS_SELECTOR_JIT_PROFILING |
401 | ruleData.compiledSelectorUsed(); |
402 | #endif |
403 | bool selectorMatches = selectorChecker(&m_element, &specificity); |
404 | |
405 | if (selectorMatches && ruleData.containsUncommonAttributeSelector()) |
406 | m_didMatchUncommonAttributeSelector = true; |
407 | |
408 | return selectorMatches; |
409 | } |
410 | #endif // ENABLE(CSS_SELECTOR_JIT) |
411 | |
412 | SelectorChecker::CheckingContext context(m_mode); |
413 | context.pseudoId = m_pseudoStyleRequest.pseudoId; |
414 | context.scrollbar = m_pseudoStyleRequest.scrollbar; |
415 | context.scrollbarPart = m_pseudoStyleRequest.scrollbarPart; |
416 | context.isMatchingHostPseudoClass = m_isMatchingHostPseudoClass; |
417 | |
418 | bool selectorMatches; |
419 | #if ENABLE(CSS_SELECTOR_JIT) |
420 | if (compiledSelectorChecker) { |
421 | ASSERT(compiledSelector.status == SelectorCompilationStatus::SelectorCheckerWithCheckingContext); |
422 | |
423 | auto selectorChecker = SelectorCompiler::ruleCollectorSelectorCheckerFunctionWithCheckingContext(compiledSelectorChecker, compiledSelector.status); |
424 | |
425 | #if CSS_SELECTOR_JIT_PROFILING |
426 | compiledSelector.useCount++; |
427 | #endif |
428 | selectorMatches = selectorChecker(&m_element, &context, &specificity); |
429 | } else |
430 | #endif // ENABLE(CSS_SELECTOR_JIT) |
431 | { |
432 | auto* selector = ruleData.selector(); |
433 | if (m_isMatchingSlottedPseudoElements) { |
434 | selector = findSlottedPseudoElementSelector(ruleData.selector()); |
435 | if (!selector) |
436 | return false; |
437 | } |
438 | // Slow path. |
439 | SelectorChecker selectorChecker(m_element.document()); |
440 | selectorMatches = selectorChecker.match(*selector, m_element, context, specificity); |
441 | } |
442 | |
443 | if (ruleData.containsUncommonAttributeSelector()) { |
444 | if (selectorMatches || context.pseudoIDSet) |
445 | m_didMatchUncommonAttributeSelector = true; |
446 | } |
447 | m_matchedPseudoElementIds.merge(context.pseudoIDSet); |
448 | m_styleRelations.appendVector(context.styleRelations); |
449 | |
450 | return selectorMatches; |
451 | } |
452 | |
453 | void ElementRuleCollector::collectMatchingRulesForList(const RuleSet::RuleDataVector* rules, const MatchRequest& matchRequest, StyleResolver::RuleRange& ruleRange) |
454 | { |
455 | if (!rules) |
456 | return; |
457 | |
458 | for (unsigned i = 0, size = rules->size(); i < size; ++i) { |
459 | const RuleData& ruleData = rules->data()[i]; |
460 | |
461 | if (!ruleData.canMatchPseudoElement() && m_pseudoStyleRequest.pseudoId != PseudoId::None) |
462 | continue; |
463 | |
464 | if (m_selectorFilter && m_selectorFilter->fastRejectSelector(ruleData.descendantSelectorIdentifierHashes())) |
465 | continue; |
466 | |
467 | StyleRule* rule = ruleData.rule(); |
468 | |
469 | // If the rule has no properties to apply, then ignore it in the non-debug mode. |
470 | // Note that if we get null back here, it means we have a rule with deferred properties, |
471 | // and that means we always have to consider it. |
472 | const StyleProperties* properties = rule->propertiesWithoutDeferredParsing(); |
473 | if (properties && properties->isEmpty() && !matchRequest.includeEmptyRules) |
474 | continue; |
475 | |
476 | unsigned specificity; |
477 | if (ruleMatches(ruleData, specificity)) |
478 | addMatchedRule(ruleData, specificity, matchRequest.styleScopeOrdinal, ruleRange); |
479 | } |
480 | } |
481 | |
482 | static inline bool compareRules(MatchedRule r1, MatchedRule r2) |
483 | { |
484 | // For normal properties the earlier scope wins. This may be reversed by !important which is handled when resolving cascade. |
485 | if (r1.styleScopeOrdinal != r2.styleScopeOrdinal) |
486 | return r1.styleScopeOrdinal > r2.styleScopeOrdinal; |
487 | |
488 | if (r1.specificity != r2.specificity) |
489 | return r1.specificity < r2.specificity; |
490 | |
491 | return r1.ruleData->position() < r2.ruleData->position(); |
492 | } |
493 | |
494 | void ElementRuleCollector::sortMatchedRules() |
495 | { |
496 | std::sort(m_matchedRules.begin(), m_matchedRules.end(), compareRules); |
497 | } |
498 | |
499 | void ElementRuleCollector::matchAllRules(bool matchAuthorAndUserStyles, bool includeSMILProperties) |
500 | { |
501 | matchUARules(); |
502 | |
503 | // Now we check user sheet rules. |
504 | if (matchAuthorAndUserStyles) |
505 | matchUserRules(false); |
506 | |
507 | // Now check author rules, beginning first with presentational attributes mapped from HTML. |
508 | if (is<StyledElement>(m_element)) { |
509 | auto& styledElement = downcast<StyledElement>(m_element); |
510 | addElementStyleProperties(styledElement.presentationAttributeStyle()); |
511 | |
512 | // Now we check additional mapped declarations. |
513 | // Tables and table cells share an additional mapped rule that must be applied |
514 | // after all attributes, since their mapped style depends on the values of multiple attributes. |
515 | addElementStyleProperties(styledElement.additionalPresentationAttributeStyle()); |
516 | |
517 | if (is<HTMLElement>(styledElement)) { |
518 | bool isAuto; |
519 | TextDirection textDirection = downcast<HTMLElement>(styledElement).directionalityIfhasDirAutoAttribute(isAuto); |
520 | if (isAuto) |
521 | m_result.addMatchedProperties(textDirection == TextDirection::LTR ? leftToRightDeclaration() : rightToLeftDeclaration()); |
522 | } |
523 | } |
524 | |
525 | // Check the rules in author sheets next. |
526 | if (matchAuthorAndUserStyles) |
527 | matchAuthorRules(false); |
528 | |
529 | if (matchAuthorAndUserStyles && is<StyledElement>(m_element)) { |
530 | auto& styledElement = downcast<StyledElement>(m_element); |
531 | // Now check our inline style attribute. |
532 | if (styledElement.inlineStyle()) { |
533 | // Inline style is immutable as long as there is no CSSOM wrapper. |
534 | // FIXME: Media control shadow trees seem to have problems with caching. |
535 | bool isInlineStyleCacheable = !styledElement.inlineStyle()->isMutable() && !styledElement.isInShadowTree(); |
536 | // FIXME: Constify. |
537 | addElementStyleProperties(styledElement.inlineStyle(), isInlineStyleCacheable); |
538 | } |
539 | |
540 | // Now check SMIL animation override style. |
541 | if (includeSMILProperties && is<SVGElement>(styledElement)) |
542 | addElementStyleProperties(downcast<SVGElement>(styledElement).animatedSMILStyleProperties(), false /* isCacheable */); |
543 | } |
544 | } |
545 | |
546 | bool ElementRuleCollector::hasAnyMatchingRules(const RuleSet* ruleSet) |
547 | { |
548 | clearMatchedRules(); |
549 | |
550 | m_mode = SelectorChecker::Mode::CollectingRulesIgnoringVirtualPseudoElements; |
551 | int firstRuleIndex = -1, lastRuleIndex = -1; |
552 | StyleResolver::RuleRange ruleRange(firstRuleIndex, lastRuleIndex); |
553 | collectMatchingRules(MatchRequest(ruleSet), ruleRange); |
554 | |
555 | return !m_matchedRules.isEmpty(); |
556 | } |
557 | |
558 | } // namespace WebCore |
559 | |