PresentationAttributeCacheCleaner::m_hitCount is never initialized
[WebKit-https.git] / Source / WebCore / dom / StyledElement.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  *           (C) 2001 Peter Kelly (pmk@post.com)
5  *           (C) 2001 Dirk Mueller (mueller@kde.org)
6  * Copyright (C) 2004, 2005, 2006, 2008, 2010 Apple Inc. All rights reserved.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23
24 #include "config.h"
25 #include "StyledElement.h"
26
27 #include "Attribute.h"
28 #include "CSSImageValue.h"
29 #include "CSSParser.h"
30 #include "CSSPropertyNames.h"
31 #include "CSSStyleSheet.h"
32 #include "CSSValueKeywords.h"
33 #include "CSSValuePool.h"
34 #include "Color.h"
35 #include "ClassList.h"
36 #include "ContentSecurityPolicy.h"
37 #include "DOMTokenList.h"
38 #include "Document.h"
39 #include "HTMLNames.h"
40 #include "HTMLParserIdioms.h"
41 #include "PropertySetCSSStyleDeclaration.h"
42 #include "ScriptableDocumentParser.h"
43 #include "StylePropertySet.h"
44 #include "StyleResolver.h"
45 #include <wtf/HashFunctions.h>
46 #include <wtf/text/TextPosition.h>
47
48 using namespace std;
49
50 namespace WebCore {
51
52 COMPILE_ASSERT(sizeof(StyledElement) == sizeof(Element), styledelement_should_remain_same_size_as_element);
53
54 using namespace HTMLNames;
55
56 struct PresentationAttributeCacheKey {
57     PresentationAttributeCacheKey() : tagName(0) { }
58     AtomicStringImpl* tagName;
59     // Only the values need refcounting.
60     Vector<pair<AtomicStringImpl*, AtomicString>, 3> attributesAndValues;
61 };
62
63 struct PresentationAttributeCacheEntry {
64     WTF_MAKE_FAST_ALLOCATED;
65 public:
66     PresentationAttributeCacheKey key;
67     RefPtr<StylePropertySet> value;
68 };
69
70 typedef HashMap<unsigned, OwnPtr<PresentationAttributeCacheEntry>, AlreadyHashed> PresentationAttributeCache;
71     
72 static bool operator!=(const PresentationAttributeCacheKey& a, const PresentationAttributeCacheKey& b)
73 {
74     if (a.tagName != b.tagName)
75         return true;
76     return a.attributesAndValues != b.attributesAndValues;
77 }
78
79 static PresentationAttributeCache& presentationAttributeCache()
80 {
81     DEFINE_STATIC_LOCAL(PresentationAttributeCache, cache, ());
82     return cache;
83 }
84
85 class PresentationAttributeCacheCleaner {
86     WTF_MAKE_NONCOPYABLE(PresentationAttributeCacheCleaner); WTF_MAKE_FAST_ALLOCATED;
87 public:
88     PresentationAttributeCacheCleaner()
89         : m_hitCount(0)
90         , m_cleanTimer(this, &PresentationAttributeCacheCleaner::cleanCache)
91     {
92     }
93
94     void didHitPresentationAttributeCache()
95     {
96         if (presentationAttributeCache().size() < minimumPresentationAttributeCacheSizeForCleaning)
97             return;
98
99         m_hitCount++;
100
101         if (!m_cleanTimer.isActive())
102             m_cleanTimer.startOneShot(presentationAttributeCacheCleanTimeInSeconds);
103      }
104
105 private:
106     static const unsigned presentationAttributeCacheCleanTimeInSeconds = 60;
107     static const int minimumPresentationAttributeCacheSizeForCleaning = 100;
108     static const unsigned minimumPresentationAttributeCacheHitCountPerMinute = (100 * presentationAttributeCacheCleanTimeInSeconds) / 60;
109
110     void cleanCache(Timer<PresentationAttributeCacheCleaner>* timer)
111     {
112         ASSERT_UNUSED(timer, timer == &m_cleanTimer);
113         unsigned hitCount = m_hitCount;
114         m_hitCount = 0;
115         if (hitCount > minimumPresentationAttributeCacheHitCountPerMinute)
116             return;
117         presentationAttributeCache().clear();
118     }
119
120     unsigned m_hitCount;
121     Timer<PresentationAttributeCacheCleaner> m_cleanTimer;
122 };
123
124 static PresentationAttributeCacheCleaner& presentationAttributeCacheCleaner()
125 {
126     DEFINE_STATIC_LOCAL(PresentationAttributeCacheCleaner, cleaner, ());
127     return cleaner;
128 }
129
130 void StyledElement::synchronizeStyleAttributeInternal() const
131 {
132     ASSERT(elementData());
133     ASSERT(elementData()->m_styleAttributeIsDirty);
134     elementData()->m_styleAttributeIsDirty = false;
135     if (const StylePropertySet* inlineStyle = this->inlineStyle())
136         const_cast<StyledElement*>(this)->setSynchronizedLazyAttribute(styleAttr, inlineStyle->asText());
137 }
138
139 StyledElement::~StyledElement()
140 {
141     if (PropertySetCSSStyleDeclaration* cssomWrapper = inlineStyleCSSOMWrapper())
142         cssomWrapper->clearParentElement();
143 }
144
145 CSSStyleDeclaration* StyledElement::style()
146 {
147     return ensureMutableInlineStyle()->ensureInlineCSSStyleDeclaration(this);
148 }
149
150 MutableStylePropertySet* StyledElement::ensureMutableInlineStyle()
151 {
152     RefPtr<StylePropertySet>& inlineStyle = ensureUniqueElementData()->m_inlineStyle;
153     if (!inlineStyle)
154         inlineStyle = MutableStylePropertySet::create(strictToCSSParserMode(isHTMLElement() && !document()->inQuirksMode()));
155     else if (!inlineStyle->isMutable())
156         inlineStyle = inlineStyle->mutableCopy();
157     ASSERT(inlineStyle->isMutable());
158     return static_cast<MutableStylePropertySet*>(inlineStyle.get());
159 }
160
161 void StyledElement::attributeChanged(const QualifiedName& name, const AtomicString& newValue, AttributeModificationReason reason)
162 {
163     if (name == styleAttr)
164         styleAttributeChanged(newValue, reason);
165     else if (isPresentationAttribute(name)) {
166         elementData()->m_presentationAttributeStyleIsDirty = true;
167         setNeedsStyleRecalc(InlineStyleChange);
168     }
169
170     Element::attributeChanged(name, newValue, reason);
171 }
172
173 PropertySetCSSStyleDeclaration* StyledElement::inlineStyleCSSOMWrapper()
174 {
175     if (!inlineStyle() || !inlineStyle()->hasCSSOMWrapper())
176         return 0;
177     PropertySetCSSStyleDeclaration* cssomWrapper = ensureMutableInlineStyle()->cssStyleDeclaration();
178     ASSERT(cssomWrapper && cssomWrapper->parentElement() == this);
179     return cssomWrapper;
180 }
181
182 inline void StyledElement::setInlineStyleFromString(const AtomicString& newStyleString)
183 {
184     RefPtr<StylePropertySet>& inlineStyle = elementData()->m_inlineStyle;
185
186     // Avoid redundant work if we're using shared attribute data with already parsed inline style.
187     if (inlineStyle && !elementData()->isUnique())
188         return;
189
190     // We reconstruct the property set instead of mutating if there is no CSSOM wrapper.
191     // This makes wrapperless property sets immutable and so cacheable.
192     if (inlineStyle && !inlineStyle->isMutable())
193         inlineStyle.clear();
194
195     if (!inlineStyle)
196         inlineStyle = CSSParser::parseInlineStyleDeclaration(newStyleString, this);
197     else {
198         ASSERT(inlineStyle->isMutable());
199         static_pointer_cast<MutableStylePropertySet>(inlineStyle)->parseDeclaration(newStyleString, document()->elementSheet()->contents());
200     }
201 }
202
203 void StyledElement::styleAttributeChanged(const AtomicString& newStyleString, AttributeModificationReason reason)
204 {
205     WTF::OrdinalNumber startLineNumber = WTF::OrdinalNumber::beforeFirst();
206     if (document() && document()->scriptableDocumentParser() && !document()->isInDocumentWrite())
207         startLineNumber = document()->scriptableDocumentParser()->lineNumber();
208
209     if (newStyleString.isNull()) {
210         if (PropertySetCSSStyleDeclaration* cssomWrapper = inlineStyleCSSOMWrapper())
211             cssomWrapper->clearParentElement();
212         ensureUniqueElementData()->m_inlineStyle.clear();
213     } else if (reason == ModifiedByCloning || document()->contentSecurityPolicy()->allowInlineStyle(document()->url(), startLineNumber))
214         setInlineStyleFromString(newStyleString);
215
216     elementData()->m_styleAttributeIsDirty = false;
217
218     setNeedsStyleRecalc(InlineStyleChange);
219     InspectorInstrumentation::didInvalidateStyleAttr(document(), this);
220 }
221
222 void StyledElement::inlineStyleChanged()
223 {
224     setNeedsStyleRecalc(InlineStyleChange);
225     ASSERT(elementData());
226     elementData()->m_styleAttributeIsDirty = true;
227     InspectorInstrumentation::didInvalidateStyleAttr(document(), this);
228 }
229     
230 bool StyledElement::setInlineStyleProperty(CSSPropertyID propertyID, int identifier, bool important)
231 {
232     ensureMutableInlineStyle()->setProperty(propertyID, cssValuePool().createIdentifierValue(identifier), important);
233     inlineStyleChanged();
234     return true;
235 }
236
237 bool StyledElement::setInlineStyleProperty(CSSPropertyID propertyID, double value, CSSPrimitiveValue::UnitTypes unit, bool important)
238 {
239     ensureMutableInlineStyle()->setProperty(propertyID, cssValuePool().createValue(value, unit), important);
240     inlineStyleChanged();
241     return true;
242 }
243
244 bool StyledElement::setInlineStyleProperty(CSSPropertyID propertyID, const String& value, bool important)
245 {
246     bool changes = ensureMutableInlineStyle()->setProperty(propertyID, value, important, document()->elementSheet()->contents());
247     if (changes)
248         inlineStyleChanged();
249     return changes;
250 }
251
252 bool StyledElement::removeInlineStyleProperty(CSSPropertyID propertyID)
253 {
254     if (!inlineStyle())
255         return false;
256     bool changes = ensureMutableInlineStyle()->removeProperty(propertyID);
257     if (changes)
258         inlineStyleChanged();
259     return changes;
260 }
261
262 void StyledElement::removeAllInlineStyleProperties()
263 {
264     if (!inlineStyle() || inlineStyle()->isEmpty())
265         return;
266     ensureMutableInlineStyle()->clear();
267     inlineStyleChanged();
268 }
269
270 void StyledElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const
271 {
272     if (const StylePropertySet* inlineStyle = elementData() ? elementData()->inlineStyle() : 0)
273         inlineStyle->addSubresourceStyleURLs(urls, document()->elementSheet()->contents());
274 }
275
276 static inline bool attributeNameSort(const pair<AtomicStringImpl*, AtomicString>& p1, const pair<AtomicStringImpl*, AtomicString>& p2)
277 {
278     // Sort based on the attribute name pointers. It doesn't matter what the order is as long as it is always the same. 
279     return p1.first < p2.first;
280 }
281
282 void StyledElement::makePresentationAttributeCacheKey(PresentationAttributeCacheKey& result) const
283 {    
284     // FIXME: Enable for SVG.
285     if (namespaceURI() != xhtmlNamespaceURI)
286         return;
287     // Interpretation of the size attributes on <input> depends on the type attribute.
288     if (hasTagName(inputTag))
289         return;
290     unsigned size = attributeCount();
291     for (unsigned i = 0; i < size; ++i) {
292         const Attribute* attribute = attributeItem(i);
293         if (!isPresentationAttribute(attribute->name()))
294             continue;
295         if (!attribute->namespaceURI().isNull())
296             return;
297         // FIXME: Background URL may depend on the base URL and can't be shared. Disallow caching.
298         if (attribute->name() == backgroundAttr)
299             return;
300         result.attributesAndValues.append(make_pair(attribute->localName().impl(), attribute->value()));
301     }
302     if (result.attributesAndValues.isEmpty())
303         return;
304     // Attribute order doesn't matter. Sort for easy equality comparison.
305     std::sort(result.attributesAndValues.begin(), result.attributesAndValues.end(), attributeNameSort);
306     // The cache key is non-null when the tagName is set.
307     result.tagName = localName().impl();
308 }
309
310 static unsigned computePresentationAttributeCacheHash(const PresentationAttributeCacheKey& key)
311 {
312     if (!key.tagName)
313         return 0;
314     ASSERT(key.attributesAndValues.size());
315     unsigned attributeHash = StringHasher::hashMemory(key.attributesAndValues.data(), key.attributesAndValues.size() * sizeof(key.attributesAndValues[0]));
316     return WTF::pairIntHash(key.tagName->existingHash(), attributeHash);
317 }
318
319 void StyledElement::rebuildPresentationAttributeStyle()
320 {
321     PresentationAttributeCacheKey cacheKey;
322     makePresentationAttributeCacheKey(cacheKey);
323
324     unsigned cacheHash = computePresentationAttributeCacheHash(cacheKey);
325
326     PresentationAttributeCache::iterator cacheIterator;
327     if (cacheHash) {
328         cacheIterator = presentationAttributeCache().add(cacheHash, nullptr).iterator;
329         if (cacheIterator->value && cacheIterator->value->key != cacheKey)
330             cacheHash = 0;
331     } else
332         cacheIterator = presentationAttributeCache().end();
333
334     RefPtr<StylePropertySet> style;
335     if (cacheHash && cacheIterator->value) {
336         style = cacheIterator->value->value;
337         presentationAttributeCacheCleaner().didHitPresentationAttributeCache();
338     } else {
339         style = MutableStylePropertySet::create(isSVGElement() ? SVGAttributeMode : CSSQuirksMode);
340         unsigned size = attributeCount();
341         for (unsigned i = 0; i < size; ++i) {
342             const Attribute* attribute = attributeItem(i);
343             collectStyleForPresentationAttribute(attribute->name(), attribute->value(), static_cast<MutableStylePropertySet*>(style.get()));
344         }
345     }
346
347     // ShareableElementData doesn't store presentation attribute style, so make sure we have a UniqueElementData.
348     UniqueElementData* elementData = ensureUniqueElementData();
349
350     elementData->m_presentationAttributeStyleIsDirty = false;
351     elementData->m_presentationAttributeStyle = style->isEmpty() ? 0 : style;
352
353     if (!cacheHash || cacheIterator->value)
354         return;
355
356     OwnPtr<PresentationAttributeCacheEntry> newEntry = adoptPtr(new PresentationAttributeCacheEntry);
357     newEntry->key = cacheKey;
358     newEntry->value = style.release();
359
360     static const int presentationAttributeCacheMaximumSize = 4096;
361     if (presentationAttributeCache().size() > presentationAttributeCacheMaximumSize) {
362         // Start building from scratch if the cache ever gets big.
363         presentationAttributeCache().clear();
364         presentationAttributeCache().set(cacheHash, newEntry.release());
365     } else
366         cacheIterator->value = newEntry.release();
367 }
368
369 void StyledElement::addPropertyToPresentationAttributeStyle(MutableStylePropertySet* style, CSSPropertyID propertyID, int identifier)
370 {
371     style->setProperty(propertyID, cssValuePool().createIdentifierValue(identifier));
372 }
373
374 void StyledElement::addPropertyToPresentationAttributeStyle(MutableStylePropertySet* style, CSSPropertyID propertyID, double value, CSSPrimitiveValue::UnitTypes unit)
375 {
376     style->setProperty(propertyID, cssValuePool().createValue(value, unit));
377 }
378     
379 void StyledElement::addPropertyToPresentationAttributeStyle(MutableStylePropertySet* style, CSSPropertyID propertyID, const String& value)
380 {
381     style->setProperty(propertyID, value, false, document()->elementSheet()->contents());
382 }
383
384 }