d7c7a0e596bc0ab06ef9ca89b5d1db75176712e3
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / 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 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 "core/css/ElementRuleCollector.h"
31
32 #include "core/css/CSSImportRule.h"
33 #include "core/css/CSSKeyframesRule.h"
34 #include "core/css/CSSMediaRule.h"
35 #include "core/css/CSSRuleList.h"
36 #include "core/css/CSSSelector.h"
37 #include "core/css/CSSStyleRule.h"
38 #include "core/css/CSSStyleSheet.h"
39 #include "core/css/CSSSupportsRule.h"
40 #include "core/css/SiblingTraversalStrategies.h"
41 #include "core/css/StylePropertySet.h"
42 #include "core/css/resolver/StyleResolver.h"
43 #include "core/dom/shadow/ShadowRoot.h"
44 #include "core/rendering/style/StyleInheritedData.h"
45
46 namespace blink {
47
48 ElementRuleCollector::ElementRuleCollector(const ElementResolveContext& context,
49     const SelectorFilter& filter, RenderStyle* style)
50     : m_context(context)
51     , m_selectorFilter(filter)
52     , m_style(style)
53     , m_pseudoStyleRequest(NOPSEUDO)
54     , m_mode(SelectorChecker::ResolvingStyle)
55     , m_canUseFastReject(m_selectorFilter.parentStackIsConsistent(context.parentNode()))
56     , m_sameOriginOnly(false)
57     , m_matchingUARules(false)
58     , m_scopeContainsLastMatchedElement(false)
59 { }
60
61 ElementRuleCollector::~ElementRuleCollector()
62 {
63 }
64
65 MatchResult& ElementRuleCollector::matchedResult()
66 {
67     return m_result;
68 }
69
70 PassRefPtrWillBeRawPtr<StyleRuleList> ElementRuleCollector::matchedStyleRuleList()
71 {
72     ASSERT(m_mode == SelectorChecker::CollectingStyleRules);
73     return m_styleRuleList.release();
74 }
75
76 PassRefPtrWillBeRawPtr<CSSRuleList> ElementRuleCollector::matchedCSSRuleList()
77 {
78     ASSERT(m_mode == SelectorChecker::CollectingCSSRules);
79     return m_cssRuleList.release();
80 }
81
82 inline void ElementRuleCollector::addMatchedRule(const RuleData* rule, unsigned specificity, CascadeScope cascadeScope, CascadeOrder cascadeOrder, unsigned styleSheetIndex, const CSSStyleSheet* parentStyleSheet)
83 {
84     if (!m_matchedRules)
85         m_matchedRules = adoptPtrWillBeNoop(new WillBeHeapVector<MatchedRule, 32>);
86     m_matchedRules->append(MatchedRule(rule, specificity, cascadeScope, cascadeOrder, styleSheetIndex, parentStyleSheet));
87 }
88
89 void ElementRuleCollector::clearMatchedRules()
90 {
91     if (!m_matchedRules)
92         return;
93     m_matchedRules->clear();
94 }
95
96 inline StyleRuleList* ElementRuleCollector::ensureStyleRuleList()
97 {
98     if (!m_styleRuleList)
99         m_styleRuleList = StyleRuleList::create();
100     return m_styleRuleList.get();
101 }
102
103 inline StaticCSSRuleList* ElementRuleCollector::ensureRuleList()
104 {
105     if (!m_cssRuleList)
106         m_cssRuleList = StaticCSSRuleList::create();
107     return m_cssRuleList.get();
108 }
109
110 void ElementRuleCollector::addElementStyleProperties(const StylePropertySet* propertySet, bool isCacheable)
111 {
112     if (!propertySet)
113         return;
114     m_result.ranges.lastAuthorRule = m_result.matchedProperties.size();
115     if (m_result.ranges.firstAuthorRule == -1)
116         m_result.ranges.firstAuthorRule = m_result.ranges.lastAuthorRule;
117     m_result.addMatchedProperties(propertySet);
118     if (!isCacheable)
119         m_result.isCacheable = false;
120 }
121
122 static bool rulesApplicableInCurrentTreeScope(const Element* element, const ContainerNode* scopingNode, bool matchingTreeBoundaryRules)
123 {
124     // [skipped, because already checked] a) it's a UA rule
125     // b) We're mathcing tree boundary rules.
126     if (matchingTreeBoundaryRules)
127         return true;
128     // c) the rules comes from a scoped style sheet within the same tree scope
129     if (!scopingNode || element->treeScope() == scopingNode->treeScope())
130         return true;
131     // d) the rules comes from a scoped style sheet within an active shadow root whose host is the given element
132     if (SelectorChecker::isHostInItsShadowTree(*element, scopingNode))
133         return true;
134     return false;
135 }
136
137 void ElementRuleCollector::collectMatchingRules(const MatchRequest& matchRequest, RuleRange& ruleRange, CascadeScope cascadeScope, CascadeOrder cascadeOrder, bool matchingTreeBoundaryRules)
138 {
139     ASSERT(matchRequest.ruleSet);
140     ASSERT(m_context.element());
141
142     Element& element = *m_context.element();
143     const AtomicString& pseudoId = element.shadowPseudoId();
144     if (!pseudoId.isEmpty()) {
145         ASSERT(element.isStyledElement());
146         collectMatchingRulesForList(matchRequest.ruleSet->shadowPseudoElementRules(pseudoId), ignoreCascadeScope, cascadeOrder, matchRequest, ruleRange);
147     }
148
149     if (element.isVTTElement())
150         collectMatchingRulesForList(matchRequest.ruleSet->cuePseudoRules(), cascadeScope, cascadeOrder, matchRequest, ruleRange);
151     // Check whether other types of rules are applicable in the current tree scope. Criteria for this:
152     // a) it's a UA rule
153     // b) the rules comes from a scoped style sheet within the same tree scope
154     // c) the rules comes from a scoped style sheet within an active shadow root whose host is the given element
155     // d) the rules can cross boundaries
156     // b)-e) is checked in rulesApplicableInCurrentTreeScope.
157     if (!m_matchingUARules && !rulesApplicableInCurrentTreeScope(&element, matchRequest.scope, matchingTreeBoundaryRules))
158         return;
159
160     // We need to collect the rules for id, class, tag, and everything else into a buffer and
161     // then sort the buffer.
162     if (element.hasID())
163         collectMatchingRulesForList(matchRequest.ruleSet->idRules(element.idForStyleResolution()), cascadeScope, cascadeOrder, matchRequest, ruleRange);
164     if (element.isStyledElement() && element.hasClass()) {
165         for (size_t i = 0; i < element.classNames().size(); ++i)
166             collectMatchingRulesForList(matchRequest.ruleSet->classRules(element.classNames()[i]), cascadeScope, cascadeOrder, matchRequest, ruleRange);
167     }
168
169     if (element.isLink())
170         collectMatchingRulesForList(matchRequest.ruleSet->linkPseudoClassRules(), cascadeScope, cascadeOrder, matchRequest, ruleRange);
171     if (SelectorChecker::matchesFocusPseudoClass(element))
172         collectMatchingRulesForList(matchRequest.ruleSet->focusPseudoClassRules(), cascadeScope, cascadeOrder, matchRequest, ruleRange);
173     collectMatchingRulesForList(matchRequest.ruleSet->tagRules(element.localName()), cascadeScope, cascadeOrder, matchRequest, ruleRange);
174     collectMatchingRulesForList(matchRequest.ruleSet->universalRules(), cascadeScope, cascadeOrder, matchRequest, ruleRange);
175 }
176
177 CSSRuleList* ElementRuleCollector::nestedRuleList(CSSRule* rule)
178 {
179     switch (rule->type()) {
180     case CSSRule::MEDIA_RULE:
181         return toCSSMediaRule(rule)->cssRules();
182     case CSSRule::KEYFRAMES_RULE:
183         return toCSSKeyframesRule(rule)->cssRules();
184     case CSSRule::SUPPORTS_RULE:
185         return toCSSSupportsRule(rule)->cssRules();
186     default:
187         return 0;
188     }
189 }
190
191 template<class CSSRuleCollection>
192 CSSRule* ElementRuleCollector::findStyleRule(CSSRuleCollection* cssRules, StyleRule* styleRule)
193 {
194     if (!cssRules)
195         return 0;
196     CSSRule* result = 0;
197     for (unsigned i = 0; i < cssRules->length() && !result; ++i) {
198         CSSRule* cssRule = cssRules->item(i);
199         CSSRule::Type cssRuleType = cssRule->type();
200         if (cssRuleType == CSSRule::STYLE_RULE) {
201             CSSStyleRule* cssStyleRule = toCSSStyleRule(cssRule);
202             if (cssStyleRule->styleRule() == styleRule)
203                 result = cssRule;
204         } else if (cssRuleType == CSSRule::IMPORT_RULE) {
205             CSSImportRule* cssImportRule = toCSSImportRule(cssRule);
206             result = findStyleRule(cssImportRule->styleSheet(), styleRule);
207         } else {
208             result = findStyleRule(nestedRuleList(cssRule), styleRule);
209         }
210     }
211     return result;
212 }
213
214 void ElementRuleCollector::appendCSSOMWrapperForRule(CSSStyleSheet* parentStyleSheet, StyleRule* rule)
215 {
216     // |parentStyleSheet| is 0 if and only if the |rule| is coming from User Agent. In this case,
217     // it is safe to create CSSOM wrappers without parentStyleSheets as they will be used only
218     // by inspector which will not try to edit them.
219     RefPtrWillBeRawPtr<CSSRule> cssRule = nullptr;
220     if (parentStyleSheet)
221         cssRule = findStyleRule(parentStyleSheet, rule);
222     else
223         cssRule = rule->createCSSOMWrapper();
224     ASSERT(!parentStyleSheet || cssRule);
225     ensureRuleList()->rules().append(cssRule);
226 }
227
228 void ElementRuleCollector::sortAndTransferMatchedRules()
229 {
230     if (!m_matchedRules || m_matchedRules->isEmpty())
231         return;
232
233     sortMatchedRules();
234
235     WillBeHeapVector<MatchedRule, 32>& matchedRules = *m_matchedRules;
236     if (m_mode == SelectorChecker::CollectingStyleRules) {
237         for (unsigned i = 0; i < matchedRules.size(); ++i)
238             ensureStyleRuleList()->m_list.append(matchedRules[i].ruleData()->rule());
239         return;
240     }
241
242     if (m_mode == SelectorChecker::CollectingCSSRules) {
243         for (unsigned i = 0; i < matchedRules.size(); ++i)
244             appendCSSOMWrapperForRule(const_cast<CSSStyleSheet*>(matchedRules[i].parentStyleSheet()), matchedRules[i].ruleData()->rule());
245         return;
246     }
247
248     // Now transfer the set of matched rules over to our list of declarations.
249     for (unsigned i = 0; i < matchedRules.size(); i++) {
250         // FIXME: Matching should not modify the style directly.
251         const RuleData* ruleData = matchedRules[i].ruleData();
252         if (m_style && ruleData->containsUncommonAttributeSelector())
253             m_style->setUnique();
254         m_result.addMatchedProperties(&ruleData->rule()->properties(), ruleData->linkMatchType(), ruleData->propertyWhitelistType(m_matchingUARules));
255     }
256 }
257
258 inline bool ElementRuleCollector::ruleMatches(const RuleData& ruleData, const ContainerNode* scope, SelectorChecker::MatchResult* result)
259 {
260     SelectorChecker selectorChecker(m_context.element()->document(), m_mode);
261     SelectorChecker::SelectorCheckingContext context(ruleData.selector(), m_context.element(), SelectorChecker::VisitedMatchEnabled);
262     context.elementStyle = m_style.get();
263     context.scope = scope;
264     context.pseudoId = m_pseudoStyleRequest.pseudoId;
265     context.scrollbar = m_pseudoStyleRequest.scrollbar;
266     context.scrollbarPart = m_pseudoStyleRequest.scrollbarPart;
267     context.isUARule = m_matchingUARules;
268     context.scopeContainsLastMatchedElement = m_scopeContainsLastMatchedElement;
269     SelectorChecker::Match match = selectorChecker.match(context, DOMSiblingTraversalStrategy(), result);
270     if (match != SelectorChecker::SelectorMatches)
271         return false;
272     if (m_pseudoStyleRequest.pseudoId != NOPSEUDO && m_pseudoStyleRequest.pseudoId != result->dynamicPseudo)
273         return false;
274     return true;
275 }
276
277 void ElementRuleCollector::collectRuleIfMatches(const RuleData& ruleData, CascadeScope cascadeScope, CascadeOrder cascadeOrder, const MatchRequest& matchRequest, RuleRange& ruleRange)
278 {
279     if (m_canUseFastReject && m_selectorFilter.fastRejectSelector<RuleData::maximumIdentifierCount>(ruleData.descendantSelectorIdentifierHashes()))
280         return;
281
282     StyleRule* rule = ruleData.rule();
283     SelectorChecker::MatchResult result;
284     if (ruleMatches(ruleData, matchRequest.scope, &result)) {
285         // If the rule has no properties to apply, then ignore it in the non-debug mode.
286         const StylePropertySet& properties = rule->properties();
287         if (properties.isEmpty() && !matchRequest.includeEmptyRules)
288             return;
289         // FIXME: Exposing the non-standard getMatchedCSSRules API to web is the only reason this is needed.
290         if (m_sameOriginOnly && !ruleData.hasDocumentSecurityOrigin())
291             return;
292
293         PseudoId dynamicPseudo = result.dynamicPseudo;
294         // If we're matching normal rules, set a pseudo bit if
295         // we really just matched a pseudo-element.
296         if (dynamicPseudo != NOPSEUDO && m_pseudoStyleRequest.pseudoId == NOPSEUDO) {
297             if (m_mode == SelectorChecker::CollectingCSSRules || m_mode == SelectorChecker::CollectingStyleRules)
298                 return;
299             // FIXME: Matching should not modify the style directly.
300             if (!m_style || dynamicPseudo >= FIRST_INTERNAL_PSEUDOID)
301                 return;
302             if ((dynamicPseudo == BEFORE || dynamicPseudo == AFTER) && !ruleData.rule()->properties().hasProperty(CSSPropertyContent))
303                 return;
304             m_style->setHasPseudoStyle(dynamicPseudo);
305         } else {
306             // Update our first/last rule indices in the matched rules array.
307             ++ruleRange.lastRuleIndex;
308             if (ruleRange.firstRuleIndex == -1)
309                 ruleRange.firstRuleIndex = ruleRange.lastRuleIndex;
310
311             // Add this rule to our list of matched rules.
312             addMatchedRule(&ruleData, result.specificity, cascadeScope, cascadeOrder, matchRequest.styleSheetIndex, matchRequest.styleSheet);
313             return;
314         }
315     }
316 }
317
318 static inline bool compareRules(const MatchedRule& matchedRule1, const MatchedRule& matchedRule2)
319 {
320     if (matchedRule1.cascadeScope() != matchedRule2.cascadeScope())
321         return matchedRule1.cascadeScope() > matchedRule2.cascadeScope();
322
323     unsigned specificity1 = matchedRule1.specificity();
324     unsigned specificity2 = matchedRule2.specificity();
325     if (specificity1 != specificity2)
326         return specificity1 < specificity2;
327
328     return matchedRule1.position() < matchedRule2.position();
329 }
330
331 void ElementRuleCollector::sortMatchedRules()
332 {
333     ASSERT(m_matchedRules);
334     std::sort(m_matchedRules->begin(), m_matchedRules->end(), compareRules);
335 }
336
337 bool ElementRuleCollector::hasAnyMatchingRules(RuleSet* ruleSet)
338 {
339     clearMatchedRules();
340
341     m_mode = SelectorChecker::SharingRules;
342     // To check whether a given RuleSet has any rule matching a given element,
343     // should not see the element's treescope. Because RuleSet has no
344     // information about "scope".
345     int firstRuleIndex = -1, lastRuleIndex = -1;
346     RuleRange ruleRange(firstRuleIndex, lastRuleIndex);
347     // FIXME: Verify whether it's ok to ignore CascadeScope here.
348     collectMatchingRules(MatchRequest(ruleSet), ruleRange);
349
350     return m_matchedRules && !m_matchedRules->isEmpty();
351 }
352
353 } // namespace blink