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