[CSS Shadow Parts] Support 'exportparts' attribute
[WebKit-https.git] / Source / WebCore / css / ElementRuleCollector.cpp
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         if (!m_element.partNames().isEmpty())
208             matchPartPseudoElementRules(*m_element.containingShadowRoot(), includeEmptyRules, ruleRange);
209     }
210
211     sortAndTransferMatchedRules();
212 }
213
214 void ElementRuleCollector::matchAuthorShadowPseudoElementRules(bool includeEmptyRules, StyleResolver::RuleRange& ruleRange)
215 {
216     ASSERT(m_element.isInShadowTree());
217     auto& shadowRoot = *m_element.containingShadowRoot();
218     if (shadowRoot.mode() != ShadowRootMode::UserAgent)
219         return;
220     // Look up shadow pseudo elements also from the host scope author style as they are web-exposed.
221     auto& hostAuthorRules = Style::Scope::forNode(*shadowRoot.host()).resolver().ruleSets().authorStyle();
222     MatchRequest hostAuthorRequest { &hostAuthorRules, includeEmptyRules, Style::ScopeOrdinal::ContainingHost };
223     collectMatchingShadowPseudoElementRules(hostAuthorRequest, ruleRange);
224 }
225
226 void ElementRuleCollector::matchHostPseudoClassRules(bool includeEmptyRules, StyleResolver::RuleRange& ruleRange)
227 {
228     ASSERT(m_element.shadowRoot());
229
230     auto& shadowAuthorStyle = m_element.shadowRoot()->styleScope().resolver().ruleSets().authorStyle();
231     auto& shadowHostRules = shadowAuthorStyle.hostPseudoClassRules();
232     if (shadowHostRules.isEmpty())
233         return;
234
235     SetForScope<bool> change(m_isMatchingHostPseudoClass, true);
236
237     MatchRequest hostMatchRequest { nullptr, includeEmptyRules, Style::ScopeOrdinal::Shadow };
238     collectMatchingRulesForList(&shadowHostRules, hostMatchRequest, ruleRange);
239 }
240
241 void ElementRuleCollector::matchSlottedPseudoElementRules(bool includeEmptyRules, StyleResolver::RuleRange& ruleRange)
242 {
243     auto* slot = m_element.assignedSlot();
244     auto styleScopeOrdinal = Style::ScopeOrdinal::FirstSlot;
245
246     for (; slot; slot = slot->assignedSlot(), ++styleScopeOrdinal) {
247         auto& styleScope = Style::Scope::forNode(*slot);
248         if (!styleScope.resolver().ruleSets().isAuthorStyleDefined())
249             continue;
250         // Find out if there are any ::slotted rules in the shadow tree matching the current slot.
251         // FIXME: This is really part of the slot style and could be cached when resolving it.
252         ElementRuleCollector collector(*slot, styleScope.resolver().ruleSets().authorStyle(), nullptr);
253         auto slottedPseudoElementRules = collector.collectSlottedPseudoElementRulesForSlot(includeEmptyRules);
254         if (!slottedPseudoElementRules)
255             continue;
256         // Match in the current scope.
257         SetForScope<bool> change(m_isMatchingSlottedPseudoElements, true);
258
259         MatchRequest scopeMatchRequest(nullptr, includeEmptyRules, styleScopeOrdinal);
260         collectMatchingRulesForList(slottedPseudoElementRules.get(), scopeMatchRequest, ruleRange);
261
262         m_keepAliveSlottedPseudoElementRules.append(WTFMove(slottedPseudoElementRules));
263     }
264 }
265
266 void ElementRuleCollector::matchPartPseudoElementRules(const ShadowRoot& containingShadowRoot, bool includeEmptyRules, StyleResolver::RuleRange& ruleRange)
267 {
268     ASSERT(m_element.isInShadowTree());
269     ASSERT(!m_element.partNames().isEmpty());
270
271     auto& shadowHost = *containingShadowRoot.host();
272     {
273         SetForScope<const Element*> partMatchingScope(m_shadowHostInPartRuleScope, &shadowHost);
274
275         auto& hostAuthorRules = Style::Scope::forNode(shadowHost).resolver().ruleSets().authorStyle();
276         MatchRequest hostAuthorRequest { &hostAuthorRules, includeEmptyRules, Style::ScopeOrdinal::ContainingHost };
277         collectMatchingRulesForList(&hostAuthorRules.partPseudoElementRules(), hostAuthorRequest, ruleRange);
278     }
279
280     // Element may be exposed to styling from enclosing scopes via exportparts attributes.
281     if (containingShadowRoot.partMappings().isEmpty())
282         return;
283
284     if (auto* parentShadowRoot = shadowHost.containingShadowRoot())
285         matchPartPseudoElementRules(*parentShadowRoot, includeEmptyRules, ruleRange);
286 }
287
288 void ElementRuleCollector::collectMatchingShadowPseudoElementRules(const MatchRequest& matchRequest, StyleResolver::RuleRange& ruleRange)
289 {
290     ASSERT(matchRequest.ruleSet);
291     ASSERT(m_element.containingShadowRoot()->mode() == ShadowRootMode::UserAgent);
292
293     auto& rules = *matchRequest.ruleSet;
294 #if ENABLE(VIDEO_TRACK)
295     // FXIME: WebVTT should not be done by styling UA shadow trees like this.
296     if (m_element.isWebVTTElement())
297         collectMatchingRulesForList(rules.cuePseudoRules(), matchRequest, ruleRange);
298 #endif
299     auto& pseudoId = m_element.shadowPseudoId();
300     if (!pseudoId.isEmpty())
301         collectMatchingRulesForList(rules.shadowPseudoElementRules(pseudoId), matchRequest, ruleRange);
302 }
303
304 std::unique_ptr<RuleSet::RuleDataVector> ElementRuleCollector::collectSlottedPseudoElementRulesForSlot(bool includeEmptyRules)
305 {
306     ASSERT(is<HTMLSlotElement>(m_element));
307
308     clearMatchedRules();
309
310     m_mode = SelectorChecker::Mode::CollectingRules;
311
312     // Match global author rules.
313     MatchRequest matchRequest(&m_authorStyle, includeEmptyRules);
314     StyleResolver::RuleRange ruleRange = m_result.ranges.authorRuleRange();
315     collectMatchingRulesForList(&m_authorStyle.slottedPseudoElementRules(), matchRequest, ruleRange);
316
317     if (m_matchedRules.isEmpty())
318         return { };
319
320     auto ruleDataVector = makeUnique<RuleSet::RuleDataVector>();
321     ruleDataVector->reserveInitialCapacity(m_matchedRules.size());
322     for (auto& matchedRule : m_matchedRules)
323         ruleDataVector->uncheckedAppend(*matchedRule.ruleData);
324
325     return ruleDataVector;
326 }
327
328 void ElementRuleCollector::matchUserRules(bool includeEmptyRules)
329 {
330     if (!m_userStyle)
331         return;
332     
333     clearMatchedRules();
334
335     m_result.ranges.lastUserRule = m_result.matchedProperties().size() - 1;
336     MatchRequest matchRequest(m_userStyle, includeEmptyRules);
337     StyleResolver::RuleRange ruleRange = m_result.ranges.userRuleRange();
338     collectMatchingRules(matchRequest, ruleRange);
339
340     sortAndTransferMatchedRules();
341 }
342
343 void ElementRuleCollector::matchUARules()
344 {
345     // First we match rules from the user agent sheet.
346     if (CSSDefaultStyleSheets::simpleDefaultStyleSheet)
347         m_result.isCacheable = false;
348     RuleSet* userAgentStyleSheet = m_isPrintStyle
349         ? CSSDefaultStyleSheets::defaultPrintStyle : CSSDefaultStyleSheets::defaultStyle;
350     matchUARules(*userAgentStyleSheet);
351
352     // In quirks mode, we match rules from the quirks user agent sheet.
353     if (m_element.document().inQuirksMode())
354         matchUARules(*CSSDefaultStyleSheets::defaultQuirksStyle);
355
356     if (m_userAgentMediaQueryStyle)
357         matchUARules(*m_userAgentMediaQueryStyle);
358 }
359
360 void ElementRuleCollector::matchUARules(const RuleSet& rules)
361 {
362     clearMatchedRules();
363     
364     m_result.ranges.lastUARule = m_result.matchedProperties().size() - 1;
365     StyleResolver::RuleRange ruleRange = m_result.ranges.UARuleRange();
366     collectMatchingRules(MatchRequest(&rules), ruleRange);
367
368     sortAndTransferMatchedRules();
369 }
370
371 static const CSSSelector* findSlottedPseudoElementSelector(const CSSSelector* selector)
372 {
373     for (; selector; selector = selector->tagHistory()) {
374         if (selector->match() == CSSSelector::PseudoElement && selector->pseudoElementType() == CSSSelector::PseudoElementSlotted) {
375             if (auto* list = selector->selectorList())
376                 return list->first();
377             break;
378         }
379     };
380     return nullptr;
381 }
382
383 inline bool ElementRuleCollector::ruleMatches(const RuleData& ruleData, unsigned& specificity)
384 {
385     // We know a sufficiently simple single part selector matches simply because we found it from the rule hash when filtering the RuleSet.
386     // This is limited to HTML only so we don't need to check the namespace (because of tag name match).
387     MatchBasedOnRuleHash matchBasedOnRuleHash = ruleData.matchBasedOnRuleHash();
388     if (matchBasedOnRuleHash != MatchBasedOnRuleHash::None && m_element.isHTMLElement()) {
389         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.");
390
391         switch (matchBasedOnRuleHash) {
392         case MatchBasedOnRuleHash::None:
393             ASSERT_NOT_REACHED();
394             break;
395         case MatchBasedOnRuleHash::Universal:
396             specificity = 0;
397             break;
398         case MatchBasedOnRuleHash::ClassA:
399             specificity = static_cast<unsigned>(SelectorSpecificityIncrement::ClassA);
400             break;
401         case MatchBasedOnRuleHash::ClassB:
402             specificity = static_cast<unsigned>(SelectorSpecificityIncrement::ClassB);
403             break;
404         case MatchBasedOnRuleHash::ClassC:
405             specificity = static_cast<unsigned>(SelectorSpecificityIncrement::ClassC);
406             break;
407         }
408         return true;
409     }
410
411 #if ENABLE(CSS_SELECTOR_JIT)
412     auto& compiledSelector = ruleData.rule()->compiledSelectorForListIndex(ruleData.selectorListIndex());
413     void* compiledSelectorChecker = compiledSelector.codeRef.code().executableAddress();
414     if (!compiledSelectorChecker && compiledSelector.status == SelectorCompilationStatus::NotCompiled) {
415         compiledSelector.status = SelectorCompiler::compileSelector(ruleData.selector(), SelectorCompiler::SelectorContext::RuleCollector, compiledSelector.codeRef);
416
417         compiledSelectorChecker = compiledSelector.codeRef.code().executableAddress();
418     }
419
420     if (compiledSelectorChecker && compiledSelector.status == SelectorCompilationStatus::SimpleSelectorChecker) {
421         auto selectorChecker = SelectorCompiler::ruleCollectorSimpleSelectorCheckerFunction(compiledSelectorChecker, compiledSelector.status);
422 #if !ASSERT_MSG_DISABLED
423         unsigned ignoreSpecificity;
424         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.");
425 #endif
426 #if CSS_SELECTOR_JIT_PROFILING
427         ruleData.compiledSelectorUsed();
428 #endif
429         bool selectorMatches = selectorChecker(&m_element, &specificity);
430
431         if (selectorMatches && ruleData.containsUncommonAttributeSelector())
432             m_didMatchUncommonAttributeSelector = true;
433
434         return selectorMatches;
435     }
436 #endif // ENABLE(CSS_SELECTOR_JIT)
437
438     SelectorChecker::CheckingContext context(m_mode);
439     context.pseudoId = m_pseudoStyleRequest.pseudoId;
440     context.scrollbar = m_pseudoStyleRequest.scrollbar;
441     context.scrollbarPart = m_pseudoStyleRequest.scrollbarPart;
442     context.isMatchingHostPseudoClass = m_isMatchingHostPseudoClass;
443     context.shadowHostInPartRuleScope = m_shadowHostInPartRuleScope;
444
445     bool selectorMatches;
446 #if ENABLE(CSS_SELECTOR_JIT)
447     if (compiledSelectorChecker) {
448         ASSERT(compiledSelector.status == SelectorCompilationStatus::SelectorCheckerWithCheckingContext);
449
450         auto selectorChecker = SelectorCompiler::ruleCollectorSelectorCheckerFunctionWithCheckingContext(compiledSelectorChecker, compiledSelector.status);
451
452 #if CSS_SELECTOR_JIT_PROFILING
453         compiledSelector.useCount++;
454 #endif
455         selectorMatches = selectorChecker(&m_element, &context, &specificity);
456     } else
457 #endif // ENABLE(CSS_SELECTOR_JIT)
458     {
459         auto* selector = ruleData.selector();
460         if (m_isMatchingSlottedPseudoElements) {
461             selector = findSlottedPseudoElementSelector(ruleData.selector());
462             if (!selector)
463                 return false;
464         }
465         // Slow path.
466         SelectorChecker selectorChecker(m_element.document());
467         selectorMatches = selectorChecker.match(*selector, m_element, context, specificity);
468     }
469
470     if (ruleData.containsUncommonAttributeSelector()) {
471         if (selectorMatches || context.pseudoIDSet)
472             m_didMatchUncommonAttributeSelector = true;
473     }
474     m_matchedPseudoElementIds.merge(context.pseudoIDSet);
475     m_styleRelations.appendVector(context.styleRelations);
476
477     return selectorMatches;
478 }
479
480 void ElementRuleCollector::collectMatchingRulesForList(const RuleSet::RuleDataVector* rules, const MatchRequest& matchRequest, StyleResolver::RuleRange& ruleRange)
481 {
482     if (!rules)
483         return;
484
485     for (unsigned i = 0, size = rules->size(); i < size; ++i) {
486         const RuleData& ruleData = rules->data()[i];
487
488         if (!ruleData.canMatchPseudoElement() && m_pseudoStyleRequest.pseudoId != PseudoId::None)
489             continue;
490
491         if (m_selectorFilter && m_selectorFilter->fastRejectSelector(ruleData.descendantSelectorIdentifierHashes()))
492             continue;
493
494         StyleRule* rule = ruleData.rule();
495
496         // If the rule has no properties to apply, then ignore it in the non-debug mode.
497         // Note that if we get null back here, it means we have a rule with deferred properties,
498         // and that means we always have to consider it.
499         const StyleProperties* properties = rule->propertiesWithoutDeferredParsing();
500         if (properties && properties->isEmpty() && !matchRequest.includeEmptyRules)
501             continue;
502
503         unsigned specificity;
504         if (ruleMatches(ruleData, specificity))
505             addMatchedRule(ruleData, specificity, matchRequest.styleScopeOrdinal, ruleRange);
506     }
507 }
508
509 static inline bool compareRules(MatchedRule r1, MatchedRule r2)
510 {
511     // For normal properties the earlier scope wins. This may be reversed by !important which is handled when resolving cascade.
512     if (r1.styleScopeOrdinal != r2.styleScopeOrdinal)
513         return r1.styleScopeOrdinal > r2.styleScopeOrdinal;
514
515     if (r1.specificity != r2.specificity)
516         return r1.specificity < r2.specificity;
517
518     return r1.ruleData->position() < r2.ruleData->position();
519 }
520
521 void ElementRuleCollector::sortMatchedRules()
522 {
523     std::sort(m_matchedRules.begin(), m_matchedRules.end(), compareRules);
524 }
525
526 void ElementRuleCollector::matchAllRules(bool matchAuthorAndUserStyles, bool includeSMILProperties)
527 {
528     matchUARules();
529
530     // Now we check user sheet rules.
531     if (matchAuthorAndUserStyles)
532         matchUserRules(false);
533
534     // Now check author rules, beginning first with presentational attributes mapped from HTML.
535     if (is<StyledElement>(m_element)) {
536         auto& styledElement = downcast<StyledElement>(m_element);
537         addElementStyleProperties(styledElement.presentationAttributeStyle());
538
539         // Now we check additional mapped declarations.
540         // Tables and table cells share an additional mapped rule that must be applied
541         // after all attributes, since their mapped style depends on the values of multiple attributes.
542         addElementStyleProperties(styledElement.additionalPresentationAttributeStyle());
543
544         if (is<HTMLElement>(styledElement)) {
545             bool isAuto;
546             TextDirection textDirection = downcast<HTMLElement>(styledElement).directionalityIfhasDirAutoAttribute(isAuto);
547             if (isAuto)
548                 m_result.addMatchedProperties(textDirection == TextDirection::LTR ? leftToRightDeclaration() : rightToLeftDeclaration());
549         }
550     }
551     
552     // Check the rules in author sheets next.
553     if (matchAuthorAndUserStyles)
554         matchAuthorRules(false);
555
556     if (matchAuthorAndUserStyles && is<StyledElement>(m_element)) {
557         auto& styledElement = downcast<StyledElement>(m_element);
558         // Now check our inline style attribute.
559         if (styledElement.inlineStyle()) {
560             // Inline style is immutable as long as there is no CSSOM wrapper.
561             // FIXME: Media control shadow trees seem to have problems with caching.
562             bool isInlineStyleCacheable = !styledElement.inlineStyle()->isMutable() && !styledElement.isInShadowTree();
563             // FIXME: Constify.
564             addElementStyleProperties(styledElement.inlineStyle(), isInlineStyleCacheable);
565         }
566
567         // Now check SMIL animation override style.
568         if (includeSMILProperties && is<SVGElement>(styledElement))
569             addElementStyleProperties(downcast<SVGElement>(styledElement).animatedSMILStyleProperties(), false /* isCacheable */);
570     }
571 }
572
573 bool ElementRuleCollector::hasAnyMatchingRules(const RuleSet* ruleSet)
574 {
575     clearMatchedRules();
576
577     m_mode = SelectorChecker::Mode::CollectingRulesIgnoringVirtualPseudoElements;
578     int firstRuleIndex = -1, lastRuleIndex = -1;
579     StyleResolver::RuleRange ruleRange(firstRuleIndex, lastRuleIndex);
580     collectMatchingRules(MatchRequest(ruleSet), ruleRange);
581
582     return !m_matchedRules.isEmpty();
583 }
584
585 } // namespace WebCore