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) 2013 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 "DocumentRuleSets.h" |
31 | |
32 | #include "CSSStyleSheet.h" |
33 | #include "ExtensionStyleSheets.h" |
34 | #include "MediaQueryEvaluator.h" |
35 | #include "StyleResolver.h" |
36 | #include "StyleSheetContents.h" |
37 | |
38 | namespace WebCore { |
39 | |
40 | DocumentRuleSets::DocumentRuleSets(StyleResolver& styleResolver) |
41 | : m_styleResolver(styleResolver) |
42 | { |
43 | m_authorStyle = std::make_unique<RuleSet>(); |
44 | m_authorStyle->disableAutoShrinkToFit(); |
45 | } |
46 | |
47 | DocumentRuleSets::~DocumentRuleSets() = default; |
48 | |
49 | RuleSet* DocumentRuleSets::userAgentMediaQueryStyle() const |
50 | { |
51 | // FIXME: We should have a separate types for document rule sets and shadow tree rule sets. |
52 | if (m_isForShadowScope) |
53 | return m_styleResolver.document().styleScope().resolver().ruleSets().userAgentMediaQueryStyle(); |
54 | |
55 | updateUserAgentMediaQueryStyleIfNeeded(); |
56 | return m_userAgentMediaQueryStyle.get(); |
57 | } |
58 | |
59 | void DocumentRuleSets::updateUserAgentMediaQueryStyleIfNeeded() const |
60 | { |
61 | if (!CSSDefaultStyleSheets::mediaQueryStyleSheet) |
62 | return; |
63 | |
64 | auto ruleCount = CSSDefaultStyleSheets::mediaQueryStyleSheet->ruleCount(); |
65 | if (m_userAgentMediaQueryStyle && ruleCount == m_userAgentMediaQueryRuleCountOnUpdate) |
66 | return; |
67 | m_userAgentMediaQueryRuleCountOnUpdate = ruleCount; |
68 | |
69 | #if !ASSERT_DISABLED |
70 | bool hadViewportDependentMediaQueries = m_styleResolver.hasViewportDependentMediaQueries(); |
71 | #endif |
72 | |
73 | // Media queries on user agent sheet need to evaluated in document context. They behave like author sheets in this respect. |
74 | auto& mediaQueryEvaluator = m_styleResolver.mediaQueryEvaluator(); |
75 | m_userAgentMediaQueryStyle = std::make_unique<RuleSet>(); |
76 | m_userAgentMediaQueryStyle->addRulesFromSheet(*CSSDefaultStyleSheets::mediaQueryStyleSheet, mediaQueryEvaluator, &m_styleResolver); |
77 | |
78 | // Viewport dependent queries are currently too inefficient to allow on UA sheet. |
79 | ASSERT(!m_styleResolver.hasViewportDependentMediaQueries() || hadViewportDependentMediaQueries); |
80 | } |
81 | |
82 | RuleSet* DocumentRuleSets::userStyle() const |
83 | { |
84 | if (m_usesSharedUserStyle) |
85 | return m_styleResolver.document().styleScope().resolver().ruleSets().userStyle(); |
86 | return m_userStyle.get(); |
87 | } |
88 | |
89 | void DocumentRuleSets::initializeUserStyle() |
90 | { |
91 | auto& extensionStyleSheets = m_styleResolver.document().extensionStyleSheets(); |
92 | auto& mediaQueryEvaluator = m_styleResolver.mediaQueryEvaluator(); |
93 | auto tempUserStyle = std::make_unique<RuleSet>(); |
94 | if (CSSStyleSheet* pageUserSheet = extensionStyleSheets.pageUserSheet()) |
95 | tempUserStyle->addRulesFromSheet(pageUserSheet->contents(), mediaQueryEvaluator, &m_styleResolver); |
96 | collectRulesFromUserStyleSheets(extensionStyleSheets.injectedUserStyleSheets(), *tempUserStyle, mediaQueryEvaluator, m_styleResolver); |
97 | collectRulesFromUserStyleSheets(extensionStyleSheets.documentUserStyleSheets(), *tempUserStyle, mediaQueryEvaluator, m_styleResolver); |
98 | if (tempUserStyle->ruleCount() > 0 || tempUserStyle->pageRules().size() > 0) |
99 | m_userStyle = WTFMove(tempUserStyle); |
100 | } |
101 | |
102 | void DocumentRuleSets::collectRulesFromUserStyleSheets(const Vector<RefPtr<CSSStyleSheet>>& userSheets, RuleSet& userStyle, const MediaQueryEvaluator& medium, StyleResolver& resolver) |
103 | { |
104 | for (unsigned i = 0; i < userSheets.size(); ++i) { |
105 | ASSERT(userSheets[i]->contents().isUserStyleSheet()); |
106 | userStyle.addRulesFromSheet(userSheets[i]->contents(), medium, &resolver); |
107 | } |
108 | } |
109 | |
110 | static std::unique_ptr<RuleSet> makeRuleSet(const Vector<RuleFeature>& rules) |
111 | { |
112 | size_t size = rules.size(); |
113 | if (!size) |
114 | return nullptr; |
115 | auto ruleSet = std::make_unique<RuleSet>(); |
116 | for (size_t i = 0; i < size; ++i) |
117 | ruleSet->addRule(rules[i].rule, rules[i].selectorIndex, rules[i].selectorListIndex); |
118 | ruleSet->shrinkToFit(); |
119 | return ruleSet; |
120 | } |
121 | |
122 | void DocumentRuleSets::resetAuthorStyle() |
123 | { |
124 | m_isAuthorStyleDefined = true; |
125 | m_authorStyle = std::make_unique<RuleSet>(); |
126 | m_authorStyle->disableAutoShrinkToFit(); |
127 | } |
128 | |
129 | void DocumentRuleSets::resetUserAgentMediaQueryStyle() |
130 | { |
131 | m_userAgentMediaQueryStyle = nullptr; |
132 | } |
133 | |
134 | void DocumentRuleSets::appendAuthorStyleSheets(const Vector<RefPtr<CSSStyleSheet>>& styleSheets, MediaQueryEvaluator* medium, InspectorCSSOMWrappers& inspectorCSSOMWrappers, StyleResolver* resolver) |
135 | { |
136 | // This handles sheets added to the end of the stylesheet list only. In other cases the style resolver |
137 | // needs to be reconstructed. To handle insertions too the rule order numbers would need to be updated. |
138 | for (auto& cssSheet : styleSheets) { |
139 | ASSERT(!cssSheet->disabled()); |
140 | if (cssSheet->mediaQueries() && !medium->evaluate(*cssSheet->mediaQueries(), resolver)) |
141 | continue; |
142 | m_authorStyle->addRulesFromSheet(cssSheet->contents(), *medium, resolver); |
143 | inspectorCSSOMWrappers.collectFromStyleSheetIfNeeded(cssSheet.get()); |
144 | } |
145 | m_authorStyle->shrinkToFit(); |
146 | collectFeatures(); |
147 | } |
148 | |
149 | void DocumentRuleSets::collectFeatures() const |
150 | { |
151 | m_features.clear(); |
152 | // Collect all ids and rules using sibling selectors (:first-child and similar) |
153 | // in the current set of stylesheets. Style sharing code uses this information to reject |
154 | // sharing candidates. |
155 | if (CSSDefaultStyleSheets::defaultStyle) |
156 | m_features.add(CSSDefaultStyleSheets::defaultStyle->features()); |
157 | m_defaultStyleVersionOnFeatureCollection = CSSDefaultStyleSheets::defaultStyleVersion; |
158 | |
159 | if (auto* userAgentMediaQueryStyle = this->userAgentMediaQueryStyle()) |
160 | m_features.add(userAgentMediaQueryStyle->features()); |
161 | |
162 | if (m_authorStyle) |
163 | m_features.add(m_authorStyle->features()); |
164 | if (auto* userStyle = this->userStyle()) |
165 | m_features.add(userStyle->features()); |
166 | |
167 | m_siblingRuleSet = makeRuleSet(m_features.siblingRules); |
168 | m_uncommonAttributeRuleSet = makeRuleSet(m_features.uncommonAttributeRules); |
169 | |
170 | m_classInvalidationRuleSets.clear(); |
171 | m_attributeInvalidationRuleSets.clear(); |
172 | m_cachedHasComplexSelectorsForStyleAttribute = WTF::nullopt; |
173 | |
174 | m_features.shrinkToFit(); |
175 | } |
176 | |
177 | static Vector<InvalidationRuleSet>* ensureInvalidationRuleSets(const AtomicString& key, HashMap<AtomicString, std::unique_ptr<Vector<InvalidationRuleSet>>>& ruleSetMap, const HashMap<AtomicString, std::unique_ptr<Vector<RuleFeature>>>& ruleFeatures) |
178 | { |
179 | return ruleSetMap.ensure(key, [&] () -> std::unique_ptr<Vector<InvalidationRuleSet>> { |
180 | auto* features = ruleFeatures.get(key); |
181 | if (!features) |
182 | return nullptr; |
183 | |
184 | std::array<std::unique_ptr<RuleSet>, matchElementCount> matchElementArray; |
185 | std::array<Vector<const CSSSelector*>, matchElementCount> invalidationSelectorArray; |
186 | for (auto& feature : *features) { |
187 | auto arrayIndex = static_cast<unsigned>(*feature.matchElement); |
188 | auto& ruleSet = matchElementArray[arrayIndex]; |
189 | if (!ruleSet) |
190 | ruleSet = std::make_unique<RuleSet>(); |
191 | ruleSet->addRule(feature.rule, feature.selectorIndex, feature.selectorListIndex); |
192 | if (feature.invalidationSelector) |
193 | invalidationSelectorArray[arrayIndex].append(feature.invalidationSelector); |
194 | } |
195 | auto invalidationRuleSets = std::make_unique<Vector<InvalidationRuleSet>>(); |
196 | for (unsigned i = 0; i < matchElementArray.size(); ++i) { |
197 | if (matchElementArray[i]) |
198 | invalidationRuleSets->append({ static_cast<MatchElement>(i), WTFMove(matchElementArray[i]), WTFMove(invalidationSelectorArray[i]) }); |
199 | } |
200 | return invalidationRuleSets; |
201 | }).iterator->value.get(); |
202 | } |
203 | |
204 | const Vector<InvalidationRuleSet>* DocumentRuleSets::classInvalidationRuleSets(const AtomicString& className) const |
205 | { |
206 | return ensureInvalidationRuleSets(className, m_classInvalidationRuleSets, m_features.classRules); |
207 | } |
208 | |
209 | const Vector<InvalidationRuleSet>* DocumentRuleSets::attributeInvalidationRuleSets(const AtomicString& attributeName) const |
210 | { |
211 | return ensureInvalidationRuleSets(attributeName, m_attributeInvalidationRuleSets, m_features.attributeRules); |
212 | } |
213 | |
214 | bool DocumentRuleSets::hasComplexSelectorsForStyleAttribute() const |
215 | { |
216 | auto compute = [&] { |
217 | auto* ruleSets = attributeInvalidationRuleSets(HTMLNames::styleAttr->localName()); |
218 | if (!ruleSets) |
219 | return false; |
220 | for (auto& ruleSet : *ruleSets) { |
221 | if (ruleSet.matchElement != MatchElement::Subject) |
222 | return true; |
223 | } |
224 | return false; |
225 | }; |
226 | |
227 | if (!m_cachedHasComplexSelectorsForStyleAttribute) |
228 | m_cachedHasComplexSelectorsForStyleAttribute = compute(); |
229 | |
230 | return *m_cachedHasComplexSelectorsForStyleAttribute; |
231 | } |
232 | |
233 | } // namespace WebCore |
234 | |