1/*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB.  If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 *
20 */
21
22#ifndef RuleSet_h
23#define RuleSet_h
24
25#include "core/css/CSSKeyframesRule.h"
26#include "core/css/MediaQueryEvaluator.h"
27#include "core/css/RuleFeature.h"
28#include "core/css/StyleRule.h"
29#include "core/css/resolver/MediaQueryResult.h"
30#include "wtf/Forward.h"
31#include "wtf/HashMap.h"
32#include "wtf/LinkedStack.h"
33
34namespace WebCore {
35
36enum AddRuleFlags {
37    RuleHasNoSpecialState         = 0,
38    RuleHasDocumentSecurityOrigin = 1,
39    RuleCanUseFastCheckSelector   = 1 << 1,
40    RuleIsInRegionRule            = 1 << 2,
41};
42
43enum PropertyWhitelistType {
44    PropertyWhitelistNone   = 0,
45    PropertyWhitelistRegion,
46    PropertyWhitelistCue
47};
48
49class CSSSelector;
50class MediaQueryEvaluator;
51class StyleRuleRegion;
52class StyleSheetContents;
53
54struct MinimalRuleData {
55    MinimalRuleData(StyleRule* rule, unsigned selectorIndex, AddRuleFlags flags)
56    : m_rule(rule)
57    , m_selectorIndex(selectorIndex)
58    , m_flags(flags)
59    {
60    }
61
62    StyleRule* m_rule;
63    unsigned m_selectorIndex;
64    AddRuleFlags m_flags;
65};
66
67class RuleData {
68    WTF_MAKE_FAST_ALLOCATED;
69public:
70    RuleData(StyleRule*, unsigned selectorIndex, unsigned position, AddRuleFlags);
71
72    unsigned position() const { return m_position; }
73    StyleRule* rule() const { return m_rule; }
74    const CSSSelector* selector() const { return m_rule->selectorList().selectorAt(m_selectorIndex); }
75    unsigned selectorIndex() const { return m_selectorIndex; }
76
77    bool isLastInArray() const { return m_isLastInArray; }
78    void setLastInArray(bool flag) { m_isLastInArray = flag; }
79
80    bool hasFastCheckableSelector() const { return m_hasFastCheckableSelector; }
81    bool hasMultipartSelector() const { return m_hasMultipartSelector; }
82    bool hasRightmostSelectorMatchingHTMLBasedOnRuleHash() const { return m_hasRightmostSelectorMatchingHTMLBasedOnRuleHash; }
83    bool containsUncommonAttributeSelector() const { return m_containsUncommonAttributeSelector; }
84    unsigned specificity() const { return m_specificity; }
85    unsigned linkMatchType() const { return m_linkMatchType; }
86    bool hasDocumentSecurityOrigin() const { return m_hasDocumentSecurityOrigin; }
87    PropertyWhitelistType propertyWhitelistType(bool isMatchingUARules = false) const { return isMatchingUARules ? PropertyWhitelistNone : static_cast<PropertyWhitelistType>(m_propertyWhitelistType); }
88    // Try to balance between memory usage (there can be lots of RuleData objects) and good filtering performance.
89    static const unsigned maximumIdentifierCount = 4;
90    const unsigned* descendantSelectorIdentifierHashes() const { return m_descendantSelectorIdentifierHashes; }
91
92private:
93    StyleRule* m_rule;
94    unsigned m_selectorIndex : 12;
95    unsigned m_isLastInArray : 1; // We store an array of RuleData objects in a primitive array.
96    // This number was picked fairly arbitrarily. We can probably lower it if we need to.
97    // Some simple testing showed <100,000 RuleData's on large sites.
98    unsigned m_position : 18;
99    unsigned m_hasFastCheckableSelector : 1;
100    unsigned m_specificity : 24;
101    unsigned m_hasMultipartSelector : 1;
102    unsigned m_hasRightmostSelectorMatchingHTMLBasedOnRuleHash : 1;
103    unsigned m_containsUncommonAttributeSelector : 1;
104    unsigned m_linkMatchType : 2; //  SelectorChecker::LinkMatchMask
105    unsigned m_hasDocumentSecurityOrigin : 1;
106    unsigned m_propertyWhitelistType : 2;
107    // Use plain array instead of a Vector to minimize memory overhead.
108    unsigned m_descendantSelectorIdentifierHashes[maximumIdentifierCount];
109};
110
111struct SameSizeAsRuleData {
112    void* a;
113    unsigned b;
114    unsigned c;
115    unsigned d[4];
116};
117
118COMPILE_ASSERT(sizeof(RuleData) == sizeof(SameSizeAsRuleData), RuleData_should_stay_small);
119
120class RuleSet {
121    WTF_MAKE_NONCOPYABLE(RuleSet); WTF_MAKE_FAST_ALLOCATED;
122public:
123    static PassOwnPtr<RuleSet> create() { return adoptPtr(new RuleSet); }
124
125    void addRulesFromSheet(StyleSheetContents*, const MediaQueryEvaluator&, AddRuleFlags = RuleHasNoSpecialState);
126    void addStyleRule(StyleRule*, AddRuleFlags);
127    void addRule(StyleRule*, unsigned selectorIndex, AddRuleFlags);
128
129    const RuleFeatureSet& features() const { return m_features; }
130
131    const RuleData* idRules(StringImpl* key) const { ASSERT(!m_pendingRules); return m_idRules.get(key); }
132    const RuleData* classRules(StringImpl* key) const { ASSERT(!m_pendingRules); return m_classRules.get(key); }
133    const RuleData* tagRules(StringImpl* key) const { ASSERT(!m_pendingRules); return m_tagRules.get(key); }
134    const RuleData* shadowPseudoElementRules(StringImpl* key) const { ASSERT(!m_pendingRules); return m_shadowPseudoElementRules.get(key); }
135    const Vector<RuleData>* linkPseudoClassRules() const { ASSERT(!m_pendingRules); return &m_linkPseudoClassRules; }
136    const Vector<RuleData>* cuePseudoRules() const { ASSERT(!m_pendingRules); return &m_cuePseudoRules; }
137    const Vector<RuleData>* focusPseudoClassRules() const { ASSERT(!m_pendingRules); return &m_focusPseudoClassRules; }
138    const Vector<RuleData>* universalRules() const { ASSERT(!m_pendingRules); return &m_universalRules; }
139    const Vector<StyleRulePage*>& pageRules() const { ASSERT(!m_pendingRules); return m_pageRules; }
140    const Vector<StyleRuleViewport*>& viewportRules() const { ASSERT(!m_pendingRules); return m_viewportRules; }
141    const Vector<StyleRuleFontFace*>& fontFaceRules() const { return m_fontFaceRules; }
142    const Vector<StyleRuleKeyframes*>& keyframesRules() const { return m_keyframesRules; }
143    const Vector<MinimalRuleData>& treeBoundaryCrossingRules() const { return m_treeBoundaryCrossingRules; }
144    const Vector<MinimalRuleData>& shadowDistributedRules() const { return m_shadowDistributedRules; }
145    const MediaQueryResultList& viewportDependentMediaQueryResults() const { return m_viewportDependentMediaQueryResults; }
146
147    unsigned ruleCount() const { return m_ruleCount; }
148
149    void compactRulesIfNeeded()
150    {
151        if (!m_pendingRules)
152            return;
153        compactRules();
154    }
155
156    struct RuleSetSelectorPair {
157        RuleSetSelectorPair(const CSSSelector* selector, PassOwnPtr<RuleSet> ruleSet) : selector(selector), ruleSet(ruleSet) { }
158        RuleSetSelectorPair(const RuleSetSelectorPair& rs) : selector(rs.selector), ruleSet(const_cast<RuleSetSelectorPair*>(&rs)->ruleSet.release()) { }
159
160        const CSSSelector* selector;
161        OwnPtr<RuleSet> ruleSet;
162    };
163
164    Vector<RuleSetSelectorPair> m_regionSelectorsAndRuleSets;
165
166private:
167    typedef HashMap<StringImpl*, OwnPtr<LinkedStack<RuleData> > > PendingRuleMap;
168    typedef HashMap<StringImpl*, OwnPtr<RuleData> > CompactRuleMap;
169
170    RuleSet()
171        : m_ruleCount(0)
172    {
173    }
174
175    void addToRuleSet(StringImpl* key, PendingRuleMap&, const RuleData&);
176    void addPageRule(StyleRulePage*);
177    void addViewportRule(StyleRuleViewport*);
178    void addFontFaceRule(StyleRuleFontFace*);
179    void addKeyframesRule(StyleRuleKeyframes*);
180    void addRegionRule(StyleRuleRegion*, bool hasDocumentSecurityOrigin);
181
182    void addChildRules(const Vector<RefPtr<StyleRuleBase> >&, const MediaQueryEvaluator& medium, AddRuleFlags);
183    bool findBestRuleSetAndAdd(const CSSSelector*, RuleData&);
184
185    void compactRules();
186    static void compactPendingRules(PendingRuleMap&, CompactRuleMap&);
187
188    struct PendingRuleMaps {
189        PendingRuleMap idRules;
190        PendingRuleMap classRules;
191        PendingRuleMap tagRules;
192        PendingRuleMap shadowPseudoElementRules;
193    };
194
195    PendingRuleMaps* ensurePendingRules()
196    {
197        if (!m_pendingRules)
198            m_pendingRules = adoptPtr(new PendingRuleMaps);
199        return m_pendingRules.get();
200    }
201
202    CompactRuleMap m_idRules;
203    CompactRuleMap m_classRules;
204    CompactRuleMap m_tagRules;
205    CompactRuleMap m_shadowPseudoElementRules;
206    Vector<RuleData> m_linkPseudoClassRules;
207    Vector<RuleData> m_cuePseudoRules;
208    Vector<RuleData> m_focusPseudoClassRules;
209    Vector<RuleData> m_universalRules;
210    RuleFeatureSet m_features;
211    Vector<StyleRulePage*> m_pageRules;
212    Vector<StyleRuleViewport*> m_viewportRules;
213    Vector<StyleRuleFontFace*> m_fontFaceRules;
214    Vector<StyleRuleKeyframes*> m_keyframesRules;
215    Vector<MinimalRuleData> m_treeBoundaryCrossingRules;
216    Vector<MinimalRuleData> m_shadowDistributedRules;
217
218    MediaQueryResultList m_viewportDependentMediaQueryResults;
219
220    unsigned m_ruleCount;
221    OwnPtr<PendingRuleMaps> m_pendingRules;
222};
223
224} // namespace WebCore
225
226#endif // RuleSet_h
227