Allow implicit conversion from Ref<T> to T&
[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, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 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 "CSSRule.h"
34 #include "CSSRuleList.h"
35 #include "CSSSelector.h"
36 #include "CSSSelectorList.h"
37 #include "CSSValueKeywords.h"
38 #include "HTMLElement.h"
39 #include "InspectorInstrumentation.h"
40 #include "RenderRegion.h"
41 #include "SVGElement.h"
42 #include "SelectorCompiler.h"
43 #include "StyleProperties.h"
44 #include "StyledElement.h"
45
46 #include <wtf/TemporaryChange.h>
47
48 namespace WebCore {
49
50 static const StyleProperties& leftToRightDeclaration()
51 {
52     static NeverDestroyed<Ref<MutableStyleProperties>> leftToRightDecl(MutableStyleProperties::create());
53     if (leftToRightDecl.get()->isEmpty())
54         leftToRightDecl.get()->setProperty(CSSPropertyDirection, CSSValueLtr);
55     return leftToRightDecl.get();
56 }
57
58 static const StyleProperties& rightToLeftDeclaration()
59 {
60     static NeverDestroyed<Ref<MutableStyleProperties>> rightToLeftDecl(MutableStyleProperties::create());
61     if (rightToLeftDecl.get()->isEmpty())
62         rightToLeftDecl.get()->setProperty(CSSPropertyDirection, CSSValueRtl);
63     return rightToLeftDecl.get();
64 }
65
66 class MatchRequest {
67 public:
68     MatchRequest(RuleSet* ruleSet, bool includeEmptyRules = false)
69         : ruleSet(ruleSet)
70         , includeEmptyRules(includeEmptyRules)
71     {
72     }
73     const RuleSet* ruleSet;
74     const bool includeEmptyRules;
75 };
76
77 StyleResolver::MatchResult& ElementRuleCollector::matchedResult()
78 {
79     ASSERT(m_mode == SelectorChecker::Mode::ResolvingStyle);
80     return m_result;
81 }
82
83 const Vector<RefPtr<StyleRule>>& ElementRuleCollector::matchedRuleList() const
84 {
85     ASSERT(m_mode == SelectorChecker::Mode::CollectingRules);
86     return m_matchedRuleList;
87 }
88
89 inline void ElementRuleCollector::addMatchedRule(const RuleData* rule)
90 {
91     if (!m_matchedRules)
92         m_matchedRules = std::make_unique<Vector<const RuleData*, 32>>();
93     m_matchedRules->append(rule);
94 }
95
96 void ElementRuleCollector::clearMatchedRules()
97 {
98     if (!m_matchedRules)
99         return;
100     m_matchedRules->clear();
101 }
102
103 inline void ElementRuleCollector::addElementStyleProperties(const StyleProperties* propertySet, bool isCacheable)
104 {
105     if (!propertySet)
106         return;
107     m_result.ranges.lastAuthorRule = m_result.matchedProperties.size();
108     if (m_result.ranges.firstAuthorRule == -1)
109         m_result.ranges.firstAuthorRule = m_result.ranges.lastAuthorRule;
110     m_result.addMatchedProperties(*propertySet);
111     if (!isCacheable)
112         m_result.isCacheable = false;
113 }
114
115 class MatchingUARulesScope {
116 public:
117     MatchingUARulesScope();
118     ~MatchingUARulesScope();
119
120     static bool isMatchingUARules();
121
122 private:
123     static bool m_matchingUARules;
124 };
125
126 MatchingUARulesScope::MatchingUARulesScope()
127 {
128     ASSERT(!m_matchingUARules);
129     m_matchingUARules = true;
130 }
131
132 MatchingUARulesScope::~MatchingUARulesScope()
133 {
134     m_matchingUARules = false;
135 }
136
137 inline bool MatchingUARulesScope::isMatchingUARules()
138 {
139     return m_matchingUARules;
140 }
141
142 bool MatchingUARulesScope::m_matchingUARules = false;
143
144 void ElementRuleCollector::collectMatchingRules(const MatchRequest& matchRequest, StyleResolver::RuleRange& ruleRange)
145 {
146     ASSERT(matchRequest.ruleSet);
147     ASSERT_WITH_MESSAGE(!(m_mode == SelectorChecker::Mode::ResolvingStyle && !m_style), "When resolving style, the SelectorChecker must have a style to set the pseudo elements and/or to do marking. The SelectorCompiler also rely on that behavior.");
148     ASSERT_WITH_MESSAGE(!(m_mode == SelectorChecker::Mode::CollectingRulesIgnoringVirtualPseudoElements && m_pseudoStyleRequest.pseudoId != NOPSEUDO), "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.");
149
150 #if ENABLE(VIDEO_TRACK)
151     if (m_element.isWebVTTElement())
152         collectMatchingRulesForList(matchRequest.ruleSet->cuePseudoRules(), matchRequest, ruleRange);
153 #endif
154
155     if (m_element.isInShadowTree()) {
156         const AtomicString& pseudoId = m_element.shadowPseudoId();
157         if (!pseudoId.isEmpty())
158             collectMatchingRulesForList(matchRequest.ruleSet->shadowPseudoElementRules(pseudoId.impl()), matchRequest, ruleRange);
159
160         // Only match UA rules in shadow tree.
161         if (!MatchingUARulesScope::isMatchingUARules())
162             return;
163     }
164
165     // We need to collect the rules for id, class, tag, and everything else into a buffer and
166     // then sort the buffer.
167     if (m_element.hasID())
168         collectMatchingRulesForList(matchRequest.ruleSet->idRules(m_element.idForStyleResolution().impl()), matchRequest, ruleRange);
169     if (m_element.hasClass()) {
170         for (size_t i = 0; i < m_element.classNames().size(); ++i)
171             collectMatchingRulesForList(matchRequest.ruleSet->classRules(m_element.classNames()[i].impl()), matchRequest, ruleRange);
172     }
173
174     if (m_element.isLink())
175         collectMatchingRulesForList(matchRequest.ruleSet->linkPseudoClassRules(), matchRequest, ruleRange);
176     if (SelectorChecker::matchesFocusPseudoClass(&m_element))
177         collectMatchingRulesForList(matchRequest.ruleSet->focusPseudoClassRules(), matchRequest, ruleRange);
178     collectMatchingRulesForList(matchRequest.ruleSet->tagRules(m_element.localName().impl()), matchRequest, ruleRange);
179     collectMatchingRulesForList(matchRequest.ruleSet->universalRules(), matchRequest, ruleRange);
180 }
181
182 void ElementRuleCollector::collectMatchingRulesForRegion(const MatchRequest& matchRequest, StyleResolver::RuleRange& ruleRange)
183 {
184     if (!m_regionForStyling)
185         return;
186
187     unsigned size = matchRequest.ruleSet->regionSelectorsAndRuleSets().size();
188     for (unsigned i = 0; i < size; ++i) {
189         const CSSSelector* regionSelector = matchRequest.ruleSet->regionSelectorsAndRuleSets().at(i).selector;
190         if (checkRegionSelector(regionSelector, m_regionForStyling->generatingElement())) {
191             RuleSet* regionRules = matchRequest.ruleSet->regionSelectorsAndRuleSets().at(i).ruleSet.get();
192             ASSERT(regionRules);
193             collectMatchingRules(MatchRequest(regionRules, matchRequest.includeEmptyRules), ruleRange);
194         }
195     }
196 }
197
198 void ElementRuleCollector::sortAndTransferMatchedRules()
199 {
200     if (!m_matchedRules || m_matchedRules->isEmpty())
201         return;
202
203     sortMatchedRules();
204
205     Vector<const RuleData*, 32>& matchedRules = *m_matchedRules;
206     if (m_mode == SelectorChecker::Mode::CollectingRules) {
207         for (unsigned i = 0; i < matchedRules.size(); ++i)
208             m_matchedRuleList.append(matchedRules[i]->rule());
209         return;
210     }
211
212     // Now transfer the set of matched rules over to our list of declarations.
213     for (unsigned i = 0; i < matchedRules.size(); i++) {
214         if (m_style && matchedRules[i]->containsUncommonAttributeSelector())
215             m_style->setUnique();
216         m_result.addMatchedProperties(matchedRules[i]->rule()->properties(), matchedRules[i]->rule(), matchedRules[i]->linkMatchType(), matchedRules[i]->propertyWhitelistType(MatchingUARulesScope::isMatchingUARules()));
217     }
218 }
219
220 void ElementRuleCollector::matchAuthorRules(bool includeEmptyRules)
221 {
222     clearMatchedRules();
223     m_result.ranges.lastAuthorRule = m_result.matchedProperties.size() - 1;
224
225     // Match global author rules.
226     MatchRequest matchRequest(m_ruleSets.authorStyle(), includeEmptyRules);
227     StyleResolver::RuleRange ruleRange = m_result.ranges.authorRuleRange();
228     collectMatchingRules(matchRequest, ruleRange);
229     collectMatchingRulesForRegion(matchRequest, ruleRange);
230
231     sortAndTransferMatchedRules();
232 }
233
234 void ElementRuleCollector::matchUserRules(bool includeEmptyRules)
235 {
236     if (!m_ruleSets.userStyle())
237         return;
238     
239     clearMatchedRules();
240
241     m_result.ranges.lastUserRule = m_result.matchedProperties.size() - 1;
242     MatchRequest matchRequest(m_ruleSets.userStyle(), includeEmptyRules);
243     StyleResolver::RuleRange ruleRange = m_result.ranges.userRuleRange();
244     collectMatchingRules(matchRequest, ruleRange);
245     collectMatchingRulesForRegion(matchRequest, ruleRange);
246
247     sortAndTransferMatchedRules();
248 }
249
250 void ElementRuleCollector::matchUARules()
251 {
252     MatchingUARulesScope scope;
253
254     // First we match rules from the user agent sheet.
255     if (CSSDefaultStyleSheets::simpleDefaultStyleSheet)
256         m_result.isCacheable = false;
257     RuleSet* userAgentStyleSheet = m_isPrintStyle
258         ? CSSDefaultStyleSheets::defaultPrintStyle : CSSDefaultStyleSheets::defaultStyle;
259     matchUARules(userAgentStyleSheet);
260
261     // In quirks mode, we match rules from the quirks user agent sheet.
262     if (m_element.document().inQuirksMode())
263         matchUARules(CSSDefaultStyleSheets::defaultQuirksStyle);
264 }
265
266 void ElementRuleCollector::matchUARules(RuleSet* rules)
267 {
268     clearMatchedRules();
269     
270     m_result.ranges.lastUARule = m_result.matchedProperties.size() - 1;
271     StyleResolver::RuleRange ruleRange = m_result.ranges.UARuleRange();
272     collectMatchingRules(MatchRequest(rules), ruleRange);
273
274     sortAndTransferMatchedRules();
275 }
276
277 inline bool ElementRuleCollector::ruleMatches(const RuleData& ruleData)
278 {
279     // We know a sufficiently simple single part selector matches simply because we found it from the rule hash when filtering the RuleSet.
280     // This is limited to HTML only so we don't need to check the namespace (because of tag name match).
281     if (ruleData.hasRightmostSelectorMatchingHTMLBasedOnRuleHash() && m_element.isHTMLElement()) {
282         ASSERT_WITH_MESSAGE(m_pseudoStyleRequest.pseudoId == NOPSEUDO, "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.");
283         return true;
284     }
285
286 #if ENABLE(CSS_SELECTOR_JIT)
287     void* compiledSelectorChecker = ruleData.compiledSelectorCodeRef().code().executableAddress();
288     if (!compiledSelectorChecker && ruleData.compilationStatus() == SelectorCompilationStatus::NotCompiled) {
289         JSC::VM& vm = m_element.document().scriptExecutionContext()->vm();
290         SelectorCompilationStatus compilationStatus;
291         JSC::MacroAssemblerCodeRef compiledSelectorCodeRef;
292         compilationStatus = SelectorCompiler::compileSelector(ruleData.selector(), &vm, SelectorCompiler::SelectorContext::RuleCollector, compiledSelectorCodeRef);
293
294         ruleData.setCompiledSelector(compilationStatus, compiledSelectorCodeRef);
295         compiledSelectorChecker = ruleData.compiledSelectorCodeRef().code().executableAddress();
296     }
297
298     if (compiledSelectorChecker && ruleData.compilationStatus() == SelectorCompilationStatus::SimpleSelectorChecker) {
299         SelectorCompiler::SimpleSelectorChecker selectorChecker = SelectorCompiler::simpleSelectorCheckerFunction(compiledSelectorChecker, ruleData.compilationStatus());
300         ASSERT_WITH_MESSAGE(!selectorChecker(&m_element) || m_pseudoStyleRequest.pseudoId == NOPSEUDO, "When matching pseudo elements, we should never compile a selector checker without context unless it cannot match anything.");
301 #if CSS_SELECTOR_JIT_PROFILING
302         ruleData.compiledSelectorUsed();
303 #endif
304         return selectorChecker(&m_element);
305     }
306 #endif // ENABLE(CSS_SELECTOR_JIT)
307
308     SelectorChecker::CheckingContext context(m_mode);
309     context.elementStyle = m_style;
310     context.pseudoId = m_pseudoStyleRequest.pseudoId;
311     context.scrollbar = m_pseudoStyleRequest.scrollbar;
312     context.scrollbarPart = m_pseudoStyleRequest.scrollbarPart;
313
314 #if ENABLE(CSS_SELECTOR_JIT)
315     if (compiledSelectorChecker) {
316         ASSERT(ruleData.compilationStatus() == SelectorCompilationStatus::SelectorCheckerWithCheckingContext);
317
318         SelectorCompiler::SelectorCheckerWithCheckingContext selectorChecker = SelectorCompiler::selectorCheckerFunctionWithCheckingContext(compiledSelectorChecker, ruleData.compilationStatus());
319
320 #if CSS_SELECTOR_JIT_PROFILING
321         ruleData.compiledSelectorUsed();
322 #endif
323         return selectorChecker(&m_element, &context);
324     }
325 #endif // ENABLE(CSS_SELECTOR_JIT)
326
327     // Slow path.
328     SelectorChecker selectorChecker(m_element.document());
329     return selectorChecker.match(ruleData.selector(), &m_element, context);
330 }
331
332 void ElementRuleCollector::collectMatchingRulesForList(const Vector<RuleData>* rules, const MatchRequest& matchRequest, StyleResolver::RuleRange& ruleRange)
333 {
334     if (!rules)
335         return;
336
337     for (unsigned i = 0, size = rules->size(); i < size; ++i) {
338         const RuleData& ruleData = rules->data()[i];
339
340         if (!ruleData.canMatchPseudoElement() && m_pseudoStyleRequest.pseudoId != NOPSEUDO)
341             continue;
342
343         if (m_canUseFastReject && m_selectorFilter.fastRejectSelector<RuleData::maximumIdentifierCount>(ruleData.descendantSelectorIdentifierHashes()))
344             continue;
345
346         StyleRule* rule = ruleData.rule();
347
348         // If the rule has no properties to apply, then ignore it in the non-debug mode.
349         const StyleProperties& properties = rule->properties();
350         if (properties.isEmpty() && !matchRequest.includeEmptyRules)
351             continue;
352
353         // FIXME: Exposing the non-standard getMatchedCSSRules API to web is the only reason this is needed.
354         if (m_sameOriginOnly && !ruleData.hasDocumentSecurityOrigin())
355             continue;
356
357         if (ruleMatches(ruleData)) {
358             // Update our first/last rule indices in the matched rules array.
359             ++ruleRange.lastRuleIndex;
360             if (ruleRange.firstRuleIndex == -1)
361                 ruleRange.firstRuleIndex = ruleRange.lastRuleIndex;
362
363             // Add this rule to our list of matched rules.
364             addMatchedRule(&ruleData);
365         }
366     }
367 }
368
369 static inline bool compareRules(const RuleData* r1, const RuleData* r2)
370 {
371     unsigned specificity1 = r1->specificity();
372     unsigned specificity2 = r2->specificity();
373     return (specificity1 == specificity2) ? r1->position() < r2->position() : specificity1 < specificity2;
374 }
375
376 void ElementRuleCollector::sortMatchedRules()
377 {
378     ASSERT(m_matchedRules);
379     std::sort(m_matchedRules->begin(), m_matchedRules->end(), compareRules);
380 }
381
382 void ElementRuleCollector::matchAllRules(bool matchAuthorAndUserStyles, bool includeSMILProperties)
383 {
384     matchUARules();
385
386     // Now we check user sheet rules.
387     if (matchAuthorAndUserStyles)
388         matchUserRules(false);
389
390     // Now check author rules, beginning first with presentational attributes mapped from HTML.
391     if (is<StyledElement>(m_element)) {
392         StyledElement& styledElement = downcast<StyledElement>(m_element);
393         addElementStyleProperties(styledElement.presentationAttributeStyle());
394
395         // Now we check additional mapped declarations.
396         // Tables and table cells share an additional mapped rule that must be applied
397         // after all attributes, since their mapped style depends on the values of multiple attributes.
398         addElementStyleProperties(styledElement.additionalPresentationAttributeStyle());
399
400         if (is<HTMLElement>(styledElement)) {
401             bool isAuto;
402             TextDirection textDirection = downcast<HTMLElement>(styledElement).directionalityIfhasDirAutoAttribute(isAuto);
403             if (isAuto)
404                 m_result.addMatchedProperties(textDirection == LTR ? leftToRightDeclaration() : rightToLeftDeclaration());
405         }
406     }
407     
408     // Check the rules in author sheets next.
409     if (matchAuthorAndUserStyles)
410         matchAuthorRules(false);
411
412     if (matchAuthorAndUserStyles && is<StyledElement>(m_element)) {
413         StyledElement& styledElement = downcast<StyledElement>(m_element);
414         // Now check our inline style attribute.
415         if (styledElement.inlineStyle()) {
416             // Inline style is immutable as long as there is no CSSOM wrapper.
417             // FIXME: Media control shadow trees seem to have problems with caching.
418             bool isInlineStyleCacheable = !styledElement.inlineStyle()->isMutable() && !styledElement.isInShadowTree();
419             // FIXME: Constify.
420             addElementStyleProperties(styledElement.inlineStyle(), isInlineStyleCacheable);
421         }
422
423         // Now check SMIL animation override style.
424         if (includeSMILProperties && is<SVGElement>(styledElement))
425             addElementStyleProperties(downcast<SVGElement>(styledElement).animatedSMILStyleProperties(), false /* isCacheable */);
426     }
427 }
428
429 bool ElementRuleCollector::hasAnyMatchingRules(RuleSet* ruleSet)
430 {
431     clearMatchedRules();
432
433     m_mode = SelectorChecker::Mode::CollectingRulesIgnoringVirtualPseudoElements;
434     int firstRuleIndex = -1, lastRuleIndex = -1;
435     StyleResolver::RuleRange ruleRange(firstRuleIndex, lastRuleIndex);
436     collectMatchingRules(MatchRequest(ruleSet), ruleRange);
437
438     return m_matchedRules && !m_matchedRules->isEmpty();
439 }
440
441 } // namespace WebCore