Remove leftover cruft from scoped stylesheet implementation.
[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, 2006, 2007, 2008, 2009, 2010, 2011, 2012 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 "CSSSelector.h"
34 #include "CSSSelectorList.h"
35 #include "HTMLNames.h"
36 #include "MediaQueryEvaluator.h"
37 #include "SecurityOrigin.h"
38 #include "SelectorChecker.h"
39 #include "SelectorCheckerFastPath.h"
40 #include "SelectorFilter.h"
41 #include "StyleResolver.h"
42 #include "StyleRule.h"
43 #include "StyleRuleImport.h"
44 #include "StyleSheetContents.h"
45 #include "WebKitCSSKeyframesRule.h"
46
47 #if ENABLE(VIDEO_TRACK)
48 #include "TextTrackCue.h"
49 #endif
50
51 namespace WebCore {
52
53 using namespace HTMLNames;
54
55 // -----------------------------------------------------------------
56
57 static inline bool isSelectorMatchingHTMLBasedOnRuleHash(const CSSSelector* selector)
58 {
59     ASSERT(selector);
60     if (selector->m_match == CSSSelector::Tag) {
61         const AtomicString& selectorNamespace = selector->tagQName().namespaceURI();
62         if (selectorNamespace != starAtom && selectorNamespace != xhtmlNamespaceURI)
63             return false;
64         if (selector->relation() == CSSSelector::SubSelector)
65             return isSelectorMatchingHTMLBasedOnRuleHash(selector->tagHistory());
66         return true;
67     }
68     if (SelectorChecker::isCommonPseudoClassSelector(selector))
69         return true;
70     return selector->m_match == CSSSelector::Id || selector->m_match == CSSSelector::Class;
71 }
72
73 static inline bool selectorListContainsUncommonAttributeSelector(const CSSSelector* selector)
74 {
75     const CSSSelectorList* selectorList = selector->selectorList();
76     if (!selectorList)
77         return false;
78     for (const CSSSelector* selector = selectorList->first(); selector; selector = CSSSelectorList::next(selector)) {
79         for (const CSSSelector* component = selector; component; component = component->tagHistory()) {
80             if (component->isAttributeSelector())
81                 return true;
82         }
83     }
84     return false;
85 }
86
87 static inline bool isCommonAttributeSelectorAttribute(const QualifiedName& attribute)
88 {
89     // These are explicitly tested for equality in canShareStyleWithElement.
90     return attribute == typeAttr || attribute == readonlyAttr;
91 }
92
93 static inline bool containsUncommonAttributeSelector(const CSSSelector* selector)
94 {
95     for (; selector; selector = selector->tagHistory()) {
96         // Allow certain common attributes (used in the default style) in the selectors that match the current element.
97         if (selector->isAttributeSelector() && !isCommonAttributeSelectorAttribute(selector->attribute()))
98             return true;
99         if (selectorListContainsUncommonAttributeSelector(selector))
100             return true;
101         if (selector->relation() != CSSSelector::SubSelector) {
102             selector = selector->tagHistory();
103             break;
104         }
105     }
106
107     for (; selector; selector = selector->tagHistory()) {
108         if (selector->isAttributeSelector())
109             return true;
110         if (selectorListContainsUncommonAttributeSelector(selector))
111             return true;
112     }
113     return false;
114 }
115
116 static inline PropertyWhitelistType determinePropertyWhitelistType(const AddRuleFlags addRuleFlags, const CSSSelector* selector)
117 {
118     if (addRuleFlags & RuleIsInRegionRule)
119         return PropertyWhitelistRegion;
120 #if ENABLE(VIDEO_TRACK)
121     for (const CSSSelector* component = selector; component; component = component->tagHistory()) {
122         if (component->pseudoType() == CSSSelector::PseudoCue || (component->m_match == CSSSelector::PseudoElement && component->value() == TextTrackCue::cueShadowPseudoId()))
123             return PropertyWhitelistCue;
124     }
125 #else
126     UNUSED_PARAM(selector);
127 #endif
128     return PropertyWhitelistNone;
129 }
130
131 RuleData::RuleData(StyleRule* rule, unsigned selectorIndex, unsigned position, AddRuleFlags addRuleFlags)
132     : m_rule(rule)
133     , m_selectorIndex(selectorIndex)
134     , m_position(position)
135     , m_hasFastCheckableSelector((addRuleFlags & RuleCanUseFastCheckSelector) && SelectorCheckerFastPath::canUse(selector()))
136     , m_specificity(selector()->specificity())
137     , m_hasMultipartSelector(!!selector()->tagHistory())
138     , m_hasRightmostSelectorMatchingHTMLBasedOnRuleHash(isSelectorMatchingHTMLBasedOnRuleHash(selector()))
139     , m_containsUncommonAttributeSelector(WebCore::containsUncommonAttributeSelector(selector()))
140     , m_linkMatchType(SelectorChecker::determineLinkMatchType(selector()))
141     , m_hasDocumentSecurityOrigin(addRuleFlags & RuleHasDocumentSecurityOrigin)
142     , m_propertyWhitelistType(determinePropertyWhitelistType(addRuleFlags, selector()))
143 {
144     ASSERT(m_position == position);
145     ASSERT(m_selectorIndex == selectorIndex);
146     SelectorFilter::collectIdentifierHashes(selector(), m_descendantSelectorIdentifierHashes, maximumIdentifierCount);
147 }
148
149 static void collectFeaturesFromRuleData(RuleFeatureSet& features, const RuleData& ruleData)
150 {
151     bool foundSiblingSelector = false;
152     for (const CSSSelector* selector = ruleData.selector(); selector; selector = selector->tagHistory()) {
153         features.collectFeaturesFromSelector(selector);
154         
155         if (const CSSSelectorList* selectorList = selector->selectorList()) {
156             for (const CSSSelector* subSelector = selectorList->first(); subSelector; subSelector = CSSSelectorList::next(subSelector)) {
157                 if (!foundSiblingSelector && selector->isSiblingSelector())
158                     foundSiblingSelector = true;
159                 features.collectFeaturesFromSelector(subSelector);
160             }
161         } else if (!foundSiblingSelector && selector->isSiblingSelector())
162             foundSiblingSelector = true;
163     }
164     if (foundSiblingSelector)
165         features.siblingRules.append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.hasDocumentSecurityOrigin()));
166     if (ruleData.containsUncommonAttributeSelector())
167         features.uncommonAttributeRules.append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.hasDocumentSecurityOrigin()));
168 }
169     
170 void RuleSet::addToRuleSet(AtomicStringImpl* key, AtomRuleMap& map, const RuleData& ruleData)
171 {
172     if (!key)
173         return;
174     std::unique_ptr<Vector<RuleData>>& rules = map.add(key, nullptr).iterator->value;
175     if (!rules)
176         rules = std::make_unique<Vector<RuleData>>();
177     rules->append(ruleData);
178 }
179
180 bool RuleSet::findBestRuleSetAndAdd(const CSSSelector* component, RuleData& ruleData)
181 {
182     if (component->m_match == CSSSelector::Id) {
183         addToRuleSet(component->value().impl(), m_idRules, ruleData);
184         return true;
185     }
186     if (component->m_match == CSSSelector::Class) {
187         addToRuleSet(component->value().impl(), m_classRules, ruleData);
188         return true;
189     }
190     if (component->isCustomPseudoElement()) {
191         addToRuleSet(component->value().impl(), m_shadowPseudoElementRules, ruleData);
192         return true;
193     }
194 #if ENABLE(VIDEO_TRACK)
195     if (component->pseudoType() == CSSSelector::PseudoCue) {
196         m_cuePseudoRules.append(ruleData);
197         return true;
198     }
199 #endif
200     if (SelectorChecker::isCommonPseudoClassSelector(component)) {
201         switch (component->pseudoType()) {
202         case CSSSelector::PseudoLink:
203         case CSSSelector::PseudoVisited:
204         case CSSSelector::PseudoAnyLink:
205             m_linkPseudoClassRules.append(ruleData);
206             return true;
207         case CSSSelector::PseudoFocus:
208             m_focusPseudoClassRules.append(ruleData);
209             return true;
210         default:
211             ASSERT_NOT_REACHED();
212             return true;
213         }
214     }
215
216     if (component->m_match == CSSSelector::Tag) {
217         // If this is part of a subselector chain, recurse ahead to find a narrower set (ID/class/:pseudo)
218         if (component->relation() == CSSSelector::SubSelector) {
219             const CSSSelector* nextComponent = component->tagHistory();
220             if (nextComponent->m_match == CSSSelector::Class || nextComponent->m_match == CSSSelector::Id || SelectorChecker::isCommonPseudoClassSelector(nextComponent)) {
221                 if (findBestRuleSetAndAdd(nextComponent, ruleData))
222                     return true;
223             }
224         }
225         if (component->tagQName().localName() != starAtom) {
226             addToRuleSet(component->tagQName().localName().impl(), m_tagRules, ruleData);
227             return true;
228         }
229     }
230     return false;
231 }
232
233 void RuleSet::addRule(StyleRule* rule, unsigned selectorIndex, AddRuleFlags addRuleFlags)
234 {
235     RuleData ruleData(rule, selectorIndex, m_ruleCount++, addRuleFlags);
236     collectFeaturesFromRuleData(m_features, ruleData);
237
238     if (!findBestRuleSetAndAdd(ruleData.selector(), ruleData)) {
239         // If we didn't find a specialized map to stick it in, file under universal rules.
240         m_universalRules.append(ruleData);
241     }
242 }
243
244 void RuleSet::addPageRule(StyleRulePage* rule)
245 {
246     m_pageRules.append(rule);
247 }
248
249 void RuleSet::addRegionRule(StyleRuleRegion* regionRule, bool hasDocumentSecurityOrigin)
250 {
251     auto regionRuleSet = std::make_unique<RuleSet>();
252     // The region rule set should take into account the position inside the parent rule set.
253     // Otherwise, the rules inside region block might be incorrectly positioned before other similar rules from
254     // the stylesheet that contains the region block.
255     regionRuleSet->m_ruleCount = m_ruleCount;
256
257     // Collect the region rules into a rule set
258     // FIXME: Should this add other types of rules? (i.e. use addChildRules() directly?)
259     const Vector<RefPtr<StyleRuleBase>>& childRules = regionRule->childRules();
260     AddRuleFlags addRuleFlags = hasDocumentSecurityOrigin ? RuleHasDocumentSecurityOrigin : RuleHasNoSpecialState;
261     addRuleFlags = static_cast<AddRuleFlags>(addRuleFlags | RuleCanUseFastCheckSelector | RuleIsInRegionRule);
262     for (unsigned i = 0; i < childRules.size(); ++i) {
263         StyleRuleBase* regionStylingRule = childRules[i].get();
264         if (regionStylingRule->isStyleRule())
265             regionRuleSet->addStyleRule(static_cast<StyleRule*>(regionStylingRule), addRuleFlags);
266     }
267     // Update the "global" rule count so that proper order is maintained
268     m_ruleCount = regionRuleSet->m_ruleCount;
269
270     m_regionSelectorsAndRuleSets.append(RuleSetSelectorPair(regionRule->selectorList().first(), std::move(regionRuleSet)));
271 }
272
273 void RuleSet::addChildRules(const Vector<RefPtr<StyleRuleBase>>& rules, const MediaQueryEvaluator& medium, StyleResolver* resolver, bool hasDocumentSecurityOrigin, AddRuleFlags addRuleFlags)
274 {
275     for (unsigned i = 0; i < rules.size(); ++i) {
276         StyleRuleBase* rule = rules[i].get();
277
278         if (rule->isStyleRule()) {
279             StyleRule* styleRule = static_cast<StyleRule*>(rule);
280             addStyleRule(styleRule, addRuleFlags);
281         } else if (rule->isPageRule())
282             addPageRule(static_cast<StyleRulePage*>(rule));
283         else if (rule->isMediaRule()) {
284             StyleRuleMedia* mediaRule = static_cast<StyleRuleMedia*>(rule);
285             if ((!mediaRule->mediaQueries() || medium.eval(mediaRule->mediaQueries(), resolver)))
286                 addChildRules(mediaRule->childRules(), medium, resolver, hasDocumentSecurityOrigin, addRuleFlags);
287         } else if (rule->isFontFaceRule() && resolver) {
288             // Add this font face to our set.
289             const StyleRuleFontFace* fontFaceRule = static_cast<StyleRuleFontFace*>(rule);
290             resolver->fontSelector()->addFontFaceRule(fontFaceRule);
291             resolver->invalidateMatchedPropertiesCache();
292         } else if (rule->isKeyframesRule() && resolver) {
293             resolver->addKeyframeStyle(static_cast<StyleRuleKeyframes*>(rule));
294         }
295 #if ENABLE(CSS_REGIONS)
296         else if (rule->isRegionRule() && resolver) {
297             addRegionRule(static_cast<StyleRuleRegion*>(rule), hasDocumentSecurityOrigin);
298         }
299 #endif
300 #if ENABLE(CSS_DEVICE_ADAPTATION)
301         else if (rule->isViewportRule() && resolver) {
302             resolver->viewportStyleResolver()->addViewportRule(static_cast<StyleRuleViewport*>(rule));
303         }
304 #endif
305 #if ENABLE(CSS3_CONDITIONAL_RULES)
306         else if (rule->isSupportsRule() && static_cast<StyleRuleSupports*>(rule)->conditionIsSupported())
307             addChildRules(static_cast<StyleRuleSupports*>(rule)->childRules(), medium, resolver, hasDocumentSecurityOrigin, addRuleFlags);
308 #endif
309     }
310 }
311
312 void RuleSet::addRulesFromSheet(StyleSheetContents* sheet, const MediaQueryEvaluator& medium, StyleResolver* resolver)
313 {
314     ASSERT(sheet);
315
316     const Vector<RefPtr<StyleRuleImport>>& importRules = sheet->importRules();
317     for (unsigned i = 0; i < importRules.size(); ++i) {
318         StyleRuleImport* importRule = importRules[i].get();
319         if (importRule->styleSheet() && (!importRule->mediaQueries() || medium.eval(importRule->mediaQueries(), resolver)))
320             addRulesFromSheet(importRule->styleSheet(), medium, resolver);
321     }
322
323     bool hasDocumentSecurityOrigin = resolver && resolver->document().securityOrigin()->canRequest(sheet->baseURL());
324     AddRuleFlags addRuleFlags = static_cast<AddRuleFlags>((hasDocumentSecurityOrigin ? RuleHasDocumentSecurityOrigin : 0) | RuleCanUseFastCheckSelector);
325
326     addChildRules(sheet->childRules(), medium, resolver, hasDocumentSecurityOrigin, addRuleFlags);
327
328     if (m_autoShrinkToFitEnabled)
329         shrinkToFit();
330 }
331
332 void RuleSet::addStyleRule(StyleRule* rule, AddRuleFlags addRuleFlags)
333 {
334     for (size_t selectorIndex = 0; selectorIndex != notFound; selectorIndex = rule->selectorList().indexOfNextSelectorAfter(selectorIndex))
335         addRule(rule, selectorIndex, addRuleFlags);
336 }
337
338 static inline void shrinkMapVectorsToFit(RuleSet::AtomRuleMap& map)
339 {
340     RuleSet::AtomRuleMap::iterator end = map.end();
341     for (RuleSet::AtomRuleMap::iterator it = map.begin(); it != end; ++it)
342         it->value->shrinkToFit();
343 }
344
345 void RuleSet::shrinkToFit()
346 {
347     shrinkMapVectorsToFit(m_idRules);
348     shrinkMapVectorsToFit(m_classRules);
349     shrinkMapVectorsToFit(m_tagRules);
350     shrinkMapVectorsToFit(m_shadowPseudoElementRules);
351     m_linkPseudoClassRules.shrinkToFit();
352 #if ENABLE(VIDEO_TRACK)
353     m_cuePseudoRules.shrinkToFit();
354 #endif
355     m_focusPseudoClassRules.shrinkToFit();
356     m_universalRules.shrinkToFit();
357     m_pageRules.shrinkToFit();
358 }
359
360 } // namespace WebCore