Replace WTF::move with WTFMove
[WebKit-https.git] / Source / WebCore / css / RuleSet.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-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 "RuleSet.h"
31
32 #include "CSSFontSelector.h"
33 #include "CSSKeyframesRule.h"
34 #include "CSSSelector.h"
35 #include "CSSSelectorList.h"
36 #include "HTMLNames.h"
37 #include "MediaQueryEvaluator.h"
38 #include "SecurityOrigin.h"
39 #include "SelectorChecker.h"
40 #include "SelectorFilter.h"
41 #include "StyleResolver.h"
42 #include "StyleRule.h"
43 #include "StyleRuleImport.h"
44 #include "StyleSheetContents.h"
45
46 #if ENABLE(VIDEO_TRACK)
47 #include "TextTrackCue.h"
48 #endif
49
50 namespace WebCore {
51
52 using namespace HTMLNames;
53
54 // -----------------------------------------------------------------
55
56 static inline MatchBasedOnRuleHash computeMatchBasedOnRuleHash(const CSSSelector& selector)
57 {
58     if (selector.tagHistory())
59         return MatchBasedOnRuleHash::None;
60
61     if (selector.match() == CSSSelector::Tag) {
62         const QualifiedName& tagQualifiedName = selector.tagQName();
63         const AtomicString& selectorNamespace = tagQualifiedName.namespaceURI();
64         if (selectorNamespace == starAtom || selectorNamespace == xhtmlNamespaceURI) {
65             if (tagQualifiedName == anyQName())
66                 return MatchBasedOnRuleHash::Universal;
67             return MatchBasedOnRuleHash::ClassC;
68         }
69         return MatchBasedOnRuleHash::None;
70     }
71     if (SelectorChecker::isCommonPseudoClassSelector(&selector))
72         return MatchBasedOnRuleHash::ClassB;
73 #if ENABLE(SHADOW_DOM)
74     if (selector.match() == CSSSelector::PseudoClass && selector.pseudoClassType() == CSSSelector::PseudoClassHost)
75         return MatchBasedOnRuleHash::ClassB;
76 #endif
77     if (selector.match() == CSSSelector::Id)
78         return MatchBasedOnRuleHash::ClassA;
79     if (selector.match() == CSSSelector::Class)
80         return MatchBasedOnRuleHash::ClassB;
81     return MatchBasedOnRuleHash::None;
82 }
83
84 static bool selectorCanMatchPseudoElement(const CSSSelector& rootSelector)
85 {
86     const CSSSelector* selector = &rootSelector;
87     do {
88         if (selector->matchesPseudoElement())
89             return true;
90
91         if (const CSSSelectorList* selectorList = selector->selectorList()) {
92             for (const CSSSelector* subSelector = selectorList->first(); subSelector; subSelector = CSSSelectorList::next(subSelector)) {
93                 if (selectorCanMatchPseudoElement(*subSelector))
94                     return true;
95             }
96         }
97
98         selector = selector->tagHistory();
99     } while (selector);
100     return false;
101 }
102
103 static inline bool isCommonAttributeSelectorAttribute(const QualifiedName& attribute)
104 {
105     // These are explicitly tested for equality in canShareStyleWithElement.
106     return attribute == typeAttr || attribute == readonlyAttr;
107 }
108
109 static bool containsUncommonAttributeSelector(const CSSSelector& rootSelector, bool matchesRightmostElement)
110 {
111     const CSSSelector* selector = &rootSelector;
112     do {
113         if (selector->isAttributeSelector()) {
114             // FIXME: considering non-rightmost simple selectors is necessary because of the style sharing of cousins.
115             // It is a primitive solution which disable a lot of style sharing on pages that rely on attributes for styling.
116             // We should investigate better ways of doing this.
117             if (!isCommonAttributeSelectorAttribute(selector->attribute()) || !matchesRightmostElement)
118                 return true;
119         }
120
121         if (const CSSSelectorList* selectorList = selector->selectorList()) {
122             for (const CSSSelector* subSelector = selectorList->first(); subSelector; subSelector = CSSSelectorList::next(subSelector)) {
123                 if (containsUncommonAttributeSelector(*subSelector, matchesRightmostElement))
124                     return true;
125             }
126         }
127
128         if (selector->relation() != CSSSelector::SubSelector)
129             matchesRightmostElement = false;
130
131         selector = selector->tagHistory();
132     } while (selector);
133     return false;
134 }
135
136 static inline bool containsUncommonAttributeSelector(const CSSSelector& rootSelector)
137 {
138     return containsUncommonAttributeSelector(rootSelector, true);
139 }
140
141 static inline PropertyWhitelistType determinePropertyWhitelistType(const AddRuleFlags addRuleFlags, const CSSSelector* selector)
142 {
143     if (addRuleFlags & RuleIsInRegionRule)
144         return PropertyWhitelistRegion;
145 #if ENABLE(VIDEO_TRACK)
146     for (const CSSSelector* component = selector; component; component = component->tagHistory()) {
147         if (component->match() == CSSSelector::PseudoElement && (component->pseudoElementType() == CSSSelector::PseudoElementCue || component->value() == TextTrackCue::cueShadowPseudoId()))
148             return PropertyWhitelistCue;
149     }
150 #else
151     UNUSED_PARAM(selector);
152 #endif
153     return PropertyWhitelistNone;
154 }
155
156 RuleData::RuleData(StyleRule* rule, unsigned selectorIndex, unsigned position, AddRuleFlags addRuleFlags)
157     : m_rule(rule)
158     , m_selectorIndex(selectorIndex)
159     , m_hasDocumentSecurityOrigin(addRuleFlags & RuleHasDocumentSecurityOrigin)
160     , m_position(position)
161     , m_matchBasedOnRuleHash(static_cast<unsigned>(computeMatchBasedOnRuleHash(*selector())))
162     , m_canMatchPseudoElement(selectorCanMatchPseudoElement(*selector()))
163     , m_containsUncommonAttributeSelector(WebCore::containsUncommonAttributeSelector(*selector()))
164     , m_linkMatchType(SelectorChecker::determineLinkMatchType(selector()))
165     , m_propertyWhitelistType(determinePropertyWhitelistType(addRuleFlags, selector()))
166 #if ENABLE(CSS_SELECTOR_JIT) && CSS_SELECTOR_JIT_PROFILING
167     , m_compiledSelectorUseCount(0)
168 #endif
169 {
170     ASSERT(m_position == position);
171     ASSERT(m_selectorIndex == selectorIndex);
172     SelectorFilter::collectIdentifierHashes(selector(), m_descendantSelectorIdentifierHashes, maximumIdentifierCount);
173 }
174
175 static void collectFeaturesFromRuleData(RuleFeatureSet& features, const RuleData& ruleData)
176 {
177     bool hasSiblingSelector;
178     features.collectFeaturesFromSelector(*ruleData.selector(), hasSiblingSelector);
179
180     if (hasSiblingSelector)
181         features.siblingRules.append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.hasDocumentSecurityOrigin()));
182     if (ruleData.containsUncommonAttributeSelector())
183         features.uncommonAttributeRules.append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.hasDocumentSecurityOrigin()));
184 }
185
186 void RuleSet::addToRuleSet(AtomicStringImpl* key, AtomRuleMap& map, const RuleData& ruleData)
187 {
188     if (!key)
189         return;
190     auto& rules = map.add(key, nullptr).iterator->value;
191     if (!rules)
192         rules = std::make_unique<RuleDataVector>();
193     rules->append(ruleData);
194 }
195
196 static unsigned rulesCountForName(const RuleSet::AtomRuleMap& map, AtomicStringImpl* name)
197 {
198     if (const auto* rules = map.get(name))
199         return rules->size();
200     return 0;
201 }
202
203 void RuleSet::addRule(StyleRule* rule, unsigned selectorIndex, AddRuleFlags addRuleFlags)
204 {
205     RuleData ruleData(rule, selectorIndex, m_ruleCount++, addRuleFlags);
206     collectFeaturesFromRuleData(m_features, ruleData);
207
208     unsigned classBucketSize = 0;
209     const CSSSelector* tagSelector = nullptr;
210     const CSSSelector* classSelector = nullptr;
211     const CSSSelector* linkSelector = nullptr;
212     const CSSSelector* focusSelector = nullptr;
213     const CSSSelector* selector = ruleData.selector();
214     do {
215         if (selector->match() == CSSSelector::Id) {
216             addToRuleSet(selector->value().impl(), m_idRules, ruleData);
217             return;
218         }
219
220 #if ENABLE(VIDEO_TRACK)
221         if (selector->match() == CSSSelector::PseudoElement && selector->pseudoElementType() == CSSSelector::PseudoElementCue) {
222             m_cuePseudoRules.append(ruleData);
223             return;
224         }
225 #endif
226
227         if (selector->isCustomPseudoElement()) {
228             addToRuleSet(selector->value().impl(), m_shadowPseudoElementRules, ruleData);
229             return;
230         }
231
232         if (selector->match() == CSSSelector::Class) {
233             AtomicStringImpl* className = selector->value().impl();
234             if (!classSelector) {
235                 classSelector = selector;
236                 classBucketSize = rulesCountForName(m_classRules, className);
237             } else if (classBucketSize) {
238                 unsigned newClassBucketSize = rulesCountForName(m_classRules, className);
239                 if (newClassBucketSize < classBucketSize) {
240                     classSelector = selector;
241                     classBucketSize = newClassBucketSize;
242                 }
243             }
244         }
245
246         if (selector->match() == CSSSelector::Tag && selector->tagQName().localName() != starAtom)
247             tagSelector = selector;
248
249         if (SelectorChecker::isCommonPseudoClassSelector(selector)) {
250             switch (selector->pseudoClassType()) {
251             case CSSSelector::PseudoClassLink:
252             case CSSSelector::PseudoClassVisited:
253             case CSSSelector::PseudoClassAnyLink:
254             case CSSSelector::PseudoClassAnyLinkDeprecated:
255                 linkSelector = selector;
256                 break;
257             case CSSSelector::PseudoClassFocus:
258                 focusSelector = selector;
259                 break;
260             default:
261                 ASSERT_NOT_REACHED();
262             }
263         }
264
265 #if ENABLE(SHADOW_DOM)
266         if (selector->match() == CSSSelector::PseudoClass && selector->pseudoClassType() == CSSSelector::PseudoClassHost) {
267             m_hostPseudoClassRules.append(ruleData);
268             return;
269         }
270 #endif
271         if (selector->relation() != CSSSelector::SubSelector)
272             break;
273         selector = selector->tagHistory();
274     } while (selector);
275
276     if (classSelector) {
277         addToRuleSet(classSelector->value().impl(), m_classRules, ruleData);
278         return;
279     }
280
281     if (linkSelector) {
282         m_linkPseudoClassRules.append(ruleData);
283         return;
284     }
285
286     if (focusSelector) {
287         m_focusPseudoClassRules.append(ruleData);
288         return;
289     }
290
291     if (tagSelector) {
292         addToRuleSet(tagSelector->tagQName().localName().impl(), m_tagLocalNameRules, ruleData);
293         addToRuleSet(tagSelector->tagLowercaseLocalName().impl(), m_tagLowercaseLocalNameRules, ruleData);
294         return;
295     }
296
297     // If we didn't find a specialized map to stick it in, file under universal rules.
298     m_universalRules.append(ruleData);
299 }
300
301 void RuleSet::addPageRule(StyleRulePage* rule)
302 {
303     m_pageRules.append(rule);
304 }
305
306 void RuleSet::addRegionRule(StyleRuleRegion* regionRule, bool hasDocumentSecurityOrigin)
307 {
308     auto regionRuleSet = std::make_unique<RuleSet>();
309     // The region rule set should take into account the position inside the parent rule set.
310     // Otherwise, the rules inside region block might be incorrectly positioned before other similar rules from
311     // the stylesheet that contains the region block.
312     regionRuleSet->m_ruleCount = m_ruleCount;
313
314     // Collect the region rules into a rule set
315     // FIXME: Should this add other types of rules? (i.e. use addChildRules() directly?)
316     const Vector<RefPtr<StyleRuleBase>>& childRules = regionRule->childRules();
317     AddRuleFlags addRuleFlags = hasDocumentSecurityOrigin ? RuleHasDocumentSecurityOrigin : RuleHasNoSpecialState;
318     addRuleFlags = static_cast<AddRuleFlags>(addRuleFlags | RuleIsInRegionRule);
319     for (auto& childRule : childRules) {
320         if (is<StyleRule>(*childRule))
321             regionRuleSet->addStyleRule(downcast<StyleRule>(childRule.get()), addRuleFlags);
322     }
323     // Update the "global" rule count so that proper order is maintained
324     m_ruleCount = regionRuleSet->m_ruleCount;
325
326     m_regionSelectorsAndRuleSets.append(RuleSetSelectorPair(regionRule->selectorList().first(), WTFMove(regionRuleSet)));
327 }
328
329 void RuleSet::addChildRules(const Vector<RefPtr<StyleRuleBase>>& rules, const MediaQueryEvaluator& medium, StyleResolver* resolver, bool hasDocumentSecurityOrigin, bool isInitiatingElementInUserAgentShadowTree, AddRuleFlags addRuleFlags)
330 {
331     for (auto& rule : rules) {
332         if (is<StyleRule>(*rule))
333             addStyleRule(downcast<StyleRule>(rule.get()), addRuleFlags);
334         else if (is<StyleRulePage>(*rule))
335             addPageRule(downcast<StyleRulePage>(rule.get()));
336         else if (is<StyleRuleMedia>(*rule)) {
337             auto& mediaRule = downcast<StyleRuleMedia>(*rule);
338             if ((!mediaRule.mediaQueries() || medium.eval(mediaRule.mediaQueries(), resolver)))
339                 addChildRules(mediaRule.childRules(), medium, resolver, hasDocumentSecurityOrigin, isInitiatingElementInUserAgentShadowTree, addRuleFlags);
340         } else if (is<StyleRuleFontFace>(*rule) && resolver) {
341             // Add this font face to our set.
342             resolver->document().fontSelector().addFontFaceRule(downcast<StyleRuleFontFace>(*rule.get()), isInitiatingElementInUserAgentShadowTree);
343             resolver->invalidateMatchedPropertiesCache();
344         } else if (is<StyleRuleKeyframes>(*rule) && resolver)
345             resolver->addKeyframeStyle(downcast<StyleRuleKeyframes>(rule.get()));
346         else if (is<StyleRuleSupports>(*rule) && downcast<StyleRuleSupports>(*rule).conditionIsSupported())
347             addChildRules(downcast<StyleRuleSupports>(*rule).childRules(), medium, resolver, hasDocumentSecurityOrigin, isInitiatingElementInUserAgentShadowTree, addRuleFlags);
348 #if ENABLE(CSS_REGIONS)
349         else if (is<StyleRuleRegion>(*rule) && resolver) {
350             addRegionRule(downcast<StyleRuleRegion>(rule.get()), hasDocumentSecurityOrigin);
351         }
352 #endif
353 #if ENABLE(CSS_DEVICE_ADAPTATION)
354         else if (is<StyleRuleViewport>(*rule) && resolver) {
355             resolver->viewportStyleResolver()->addViewportRule(downcast<StyleRuleViewport>(rule.get()));
356         }
357 #endif
358     }
359 }
360
361 void RuleSet::addRulesFromSheet(StyleSheetContents& sheet, const MediaQueryEvaluator& medium, StyleResolver* resolver)
362 {
363     for (auto& rule : sheet.importRules()) {
364         if (rule->styleSheet() && (!rule->mediaQueries() || medium.eval(rule->mediaQueries(), resolver)))
365             addRulesFromSheet(*rule->styleSheet(), medium, resolver);
366     }
367
368     bool hasDocumentSecurityOrigin = resolver && resolver->document().securityOrigin()->canRequest(sheet.baseURL());
369     AddRuleFlags addRuleFlags = static_cast<AddRuleFlags>((hasDocumentSecurityOrigin ? RuleHasDocumentSecurityOrigin : 0));
370
371     // FIXME: Skip Content Security Policy check when stylesheet is in a user agent shadow tree.
372     // See <https://bugs.webkit.org/show_bug.cgi?id=146663>.
373     bool isInitiatingElementInUserAgentShadowTree = false;
374     addChildRules(sheet.childRules(), medium, resolver, hasDocumentSecurityOrigin, isInitiatingElementInUserAgentShadowTree, addRuleFlags);
375
376     if (m_autoShrinkToFitEnabled)
377         shrinkToFit();
378 }
379
380 void RuleSet::addStyleRule(StyleRule* rule, AddRuleFlags addRuleFlags)
381 {
382     for (size_t selectorIndex = 0; selectorIndex != notFound; selectorIndex = rule->selectorList().indexOfNextSelectorAfter(selectorIndex))
383         addRule(rule, selectorIndex, addRuleFlags);
384 }
385
386 bool RuleSet::hasShadowPseudoElementRules() const
387 {
388     if (!m_shadowPseudoElementRules.isEmpty())
389         return true;
390 #if ENABLE(VIDEO_TRACK)
391     if (!m_cuePseudoRules.isEmpty())
392         return true;
393 #endif
394     return false;
395 }
396
397 void RuleSet::copyShadowPseudoElementRulesFrom(const RuleSet& other)
398 {
399     for (auto& keyValuePair : other.m_shadowPseudoElementRules)
400         m_shadowPseudoElementRules.add(keyValuePair.key, std::make_unique<RuleDataVector>(*keyValuePair.value));
401
402 #if ENABLE(VIDEO_TRACK)
403     // FIXME: We probably shouldn't treat WebVTT as author stylable user agent shadow tree.
404     for (auto& cue : other.m_cuePseudoRules)
405         m_cuePseudoRules.append(cue);
406 #endif
407 }
408
409 static inline void shrinkMapVectorsToFit(RuleSet::AtomRuleMap& map)
410 {
411     for (auto& vector : map.values())
412         vector->shrinkToFit();
413 }
414
415 void RuleSet::shrinkToFit()
416 {
417     shrinkMapVectorsToFit(m_idRules);
418     shrinkMapVectorsToFit(m_classRules);
419     shrinkMapVectorsToFit(m_tagLocalNameRules);
420     shrinkMapVectorsToFit(m_tagLowercaseLocalNameRules);
421     shrinkMapVectorsToFit(m_shadowPseudoElementRules);
422     m_linkPseudoClassRules.shrinkToFit();
423 #if ENABLE(VIDEO_TRACK)
424     m_cuePseudoRules.shrinkToFit();
425 #endif
426     m_focusPseudoClassRules.shrinkToFit();
427     m_universalRules.shrinkToFit();
428     m_pageRules.shrinkToFit();
429     m_features.shrinkToFit();
430     m_regionSelectorsAndRuleSets.shrinkToFit();
431 }
432
433 } // namespace WebCore