Dropping m_startingLineNumber property from StyledElement.
[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 "CSSPropertyNames.h"
30 #include "CSSStyleSheet.h"
31 #include "CSSValueKeywords.h"
32 #include "CSSValuePool.h"
33 #include "Color.h"
34 #include "ClassList.h"
35 #include "ContentSecurityPolicy.h"
36 #include "DOMTokenList.h"
37 #include "Document.h"
38 #include "HTMLNames.h"
39 #include "HTMLParserIdioms.h"
40 #include "ScriptableDocumentParser.h"
41 #include "StylePropertySet.h"
42 #include "StyleResolver.h"
43 #include <wtf/HashFunctions.h>
44 #include <wtf/text/TextPosition.h>
45
46 using namespace std;
47
48 namespace WebCore {
49
50 using namespace HTMLNames;
51
52 struct PresentationAttributeCacheKey {
53     PresentationAttributeCacheKey() : tagName(0) { }
54     AtomicStringImpl* tagName;
55     // Only the values need refcounting.
56     Vector<pair<AtomicStringImpl*, AtomicString>, 3> attributesAndValues;
57 };
58
59 struct PresentationAttributeCacheEntry {
60     PresentationAttributeCacheKey key;
61     RefPtr<StylePropertySet> value;
62 };
63
64 typedef HashMap<unsigned, OwnPtr<PresentationAttributeCacheEntry>, AlreadyHashed> PresentationAttributeCache;
65     
66 static bool operator!=(const PresentationAttributeCacheKey& a, const PresentationAttributeCacheKey& b)
67 {
68     if (a.tagName != b.tagName)
69         return true;
70     return a.attributesAndValues != b.attributesAndValues;
71 }
72
73 static PresentationAttributeCache& presentationAttributeCache()
74 {
75     DEFINE_STATIC_LOCAL(PresentationAttributeCache, cache, ());
76     return cache;
77 }
78
79 class PresentationAttributeCacheCleaner {
80     WTF_MAKE_NONCOPYABLE(PresentationAttributeCacheCleaner);
81 public:
82     PresentationAttributeCacheCleaner()
83         : m_cleanTimer(this, &PresentationAttributeCacheCleaner::cleanCache)
84     {
85     }
86
87     void didHitPresentationAttributeCache()
88     {
89         if (presentationAttributeCache().size() < minimumPresentationAttributeCacheSizeForCleaning)
90             return;
91
92         m_hitCount++;
93
94         if (!m_cleanTimer.isActive())
95             m_cleanTimer.startOneShot(presentationAttributeCacheCleanTimeInSeconds);
96      }
97
98 private:
99     static const unsigned presentationAttributeCacheCleanTimeInSeconds = 60;
100     static const int minimumPresentationAttributeCacheSizeForCleaning = 100;
101     static const unsigned minimumPresentationAttributeCacheHitCountPerMinute = (100 * presentationAttributeCacheCleanTimeInSeconds) / 60;
102
103     void cleanCache(Timer<PresentationAttributeCacheCleaner>* timer)
104     {
105         ASSERT_UNUSED(timer, timer == &m_cleanTimer);
106         unsigned hitCount = m_hitCount;
107         m_hitCount = 0;
108         if (hitCount > minimumPresentationAttributeCacheHitCountPerMinute)
109             return;
110         presentationAttributeCache().clear();
111     }
112
113     unsigned m_hitCount;
114     Timer<PresentationAttributeCacheCleaner> m_cleanTimer;
115 };
116
117 static PresentationAttributeCacheCleaner& presentationAttributeCacheCleaner()
118 {
119     DEFINE_STATIC_LOCAL(PresentationAttributeCacheCleaner, cleaner, ());
120     return cleaner;
121 }
122
123 void StyledElement::updateStyleAttribute() const
124 {
125     ASSERT(!isStyleAttributeValid());
126     setIsStyleAttributeValid();
127     if (const StylePropertySet* inlineStyle = this->inlineStyle())
128         const_cast<StyledElement*>(this)->setAttribute(styleAttr, inlineStyle->asText(), InUpdateStyleAttribute);
129 }
130
131 StyledElement::StyledElement(const QualifiedName& name, Document* document, ConstructionType type)
132     : Element(name, document, type)
133 {
134 }
135
136 StyledElement::~StyledElement()
137 {
138     destroyInlineStyle();
139 }
140
141 CSSStyleDeclaration* StyledElement::style()
142 {
143     return ensureAttributeData()->ensureMutableInlineStyle(this)->ensureInlineCSSStyleDeclaration(this); 
144 }
145
146 void StyledElement::attributeChanged(const Attribute& attribute)
147 {
148     parseAttribute(attribute);
149
150     if (isPresentationAttribute(attribute.name())) {
151         setAttributeStyleDirty();
152         setNeedsStyleRecalc(InlineStyleChange);
153     }
154
155     Element::attributeChanged(attribute);
156 }
157
158 void StyledElement::classAttributeChanged(const AtomicString& newClassString)
159 {
160     const UChar* characters = newClassString.characters();
161     unsigned length = newClassString.length();
162     unsigned i;
163     for (i = 0; i < length; ++i) {
164         if (isNotHTMLSpace(characters[i]))
165             break;
166     }
167     bool hasClass = i < length;
168     if (hasClass) {
169         const bool shouldFoldCase = document()->inQuirksMode();
170         ensureAttributeData()->setClass(newClassString, shouldFoldCase);
171         if (DOMTokenList* classList = optionalClassList())
172             static_cast<ClassList*>(classList)->reset(newClassString);
173     } else
174         attributeData()->clearClass();
175     setNeedsStyleRecalc();
176 }
177
178 void StyledElement::styleAttributeChanged(const AtomicString& newStyleString, ShouldReparseStyleAttribute shouldReparse)
179 {
180     if (shouldReparse) {
181         WTF::OrdinalNumber startLineNumber = WTF::OrdinalNumber::beforeFirst();
182         if (document() && document()->scriptableDocumentParser() && !document()->isInDocumentWrite())
183             startLineNumber = document()->scriptableDocumentParser()->lineNumber();
184         if (newStyleString.isNull())
185             destroyInlineStyle();
186         else if (document()->contentSecurityPolicy()->allowInlineStyle(document()->url(), startLineNumber))
187             ensureAttributeData()->updateInlineStyleAvoidingMutation(this, newStyleString);
188         setIsStyleAttributeValid();
189     }
190     setNeedsStyleRecalc();
191     InspectorInstrumentation::didInvalidateStyleAttr(document(), this);
192 }
193
194 void StyledElement::parseAttribute(const Attribute& attribute)
195 {
196     if (attribute.name() == classAttr)
197         classAttributeChanged(attribute.value());
198     else if (attribute.name() == styleAttr)
199         styleAttributeChanged(attribute.value());
200 }
201
202 void StyledElement::inlineStyleChanged()
203 {
204     setNeedsStyleRecalc(InlineStyleChange);
205     setIsStyleAttributeValid(false);
206     InspectorInstrumentation::didInvalidateStyleAttr(document(), this);
207 }
208     
209 bool StyledElement::setInlineStyleProperty(CSSPropertyID propertyID, int identifier, bool important)
210 {
211     ensureAttributeData()->ensureMutableInlineStyle(this)->setProperty(propertyID, cssValuePool().createIdentifierValue(identifier), important);
212     inlineStyleChanged();
213     return true;
214 }
215
216 bool StyledElement::setInlineStyleProperty(CSSPropertyID propertyID, double value, CSSPrimitiveValue::UnitTypes unit, bool important)
217 {
218     ensureAttributeData()->ensureMutableInlineStyle(this)->setProperty(propertyID, cssValuePool().createValue(value, unit), important);
219     inlineStyleChanged();
220     return true;
221 }
222
223 bool StyledElement::setInlineStyleProperty(CSSPropertyID propertyID, const String& value, bool important)
224 {
225     bool changes = ensureAttributeData()->ensureMutableInlineStyle(this)->setProperty(propertyID, value, important, document()->elementSheet()->contents());
226     if (changes)
227         inlineStyleChanged();
228     return changes;
229 }
230
231 bool StyledElement::removeInlineStyleProperty(CSSPropertyID propertyID)
232 {
233     StylePropertySet* inlineStyle = attributeData() ? attributeData()->inlineStyle() : 0;
234     if (!inlineStyle)
235         return false;
236     bool changes = inlineStyle->removeProperty(propertyID);
237     if (changes)
238         inlineStyleChanged();
239     return changes;
240 }
241
242 void StyledElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const
243 {
244     if (StylePropertySet* inlineStyle = attributeData() ? attributeData()->inlineStyle() : 0)
245         inlineStyle->addSubresourceStyleURLs(urls, document()->elementSheet()->contents());
246 }
247
248 static inline bool attributeNameSort(const pair<AtomicStringImpl*, AtomicString>& p1, const pair<AtomicStringImpl*, AtomicString>& p2)
249 {
250     // Sort based on the attribute name pointers. It doesn't matter what the order is as long as it is always the same. 
251     return p1.first < p2.first;
252 }
253
254 void StyledElement::makePresentationAttributeCacheKey(PresentationAttributeCacheKey& result) const
255 {    
256     // FIXME: Enable for SVG.
257     if (namespaceURI() != xhtmlNamespaceURI)
258         return;
259     // Interpretation of the size attributes on <input> depends on the type attribute.
260     if (hasTagName(inputTag))
261         return;
262     unsigned size = attributeCount();
263     for (unsigned i = 0; i < size; ++i) {
264         Attribute* attribute = attributeItem(i);
265         if (!isPresentationAttribute(attribute->name()))
266             continue;
267         if (!attribute->namespaceURI().isNull())
268             return;
269         // FIXME: Background URL may depend on the base URL and can't be shared. Disallow caching.
270         if (attribute->name() == backgroundAttr)
271             return;
272         result.attributesAndValues.append(make_pair(attribute->localName().impl(), attribute->value()));
273     }
274     if (result.attributesAndValues.isEmpty())
275         return;
276     // Attribute order doesn't matter. Sort for easy equality comparison.
277     std::sort(result.attributesAndValues.begin(), result.attributesAndValues.end(), attributeNameSort);
278     // The cache key is non-null when the tagName is set.
279     result.tagName = localName().impl();
280 }
281
282 static unsigned computePresentationAttributeCacheHash(const PresentationAttributeCacheKey& key)
283 {
284     if (!key.tagName)
285         return 0;
286     ASSERT(key.attributesAndValues.size());
287     unsigned attributeHash = StringHasher::hashMemory(key.attributesAndValues.data(), key.attributesAndValues.size() * sizeof(key.attributesAndValues[0]));
288     return WTF::intHash((static_cast<uint64_t>(key.tagName->existingHash()) << 32 | attributeHash));
289 }
290
291 void StyledElement::updateAttributeStyle()
292 {
293     PresentationAttributeCacheKey cacheKey;
294     makePresentationAttributeCacheKey(cacheKey);
295
296     unsigned cacheHash = computePresentationAttributeCacheHash(cacheKey);
297
298     PresentationAttributeCache::iterator cacheIterator;
299     if (cacheHash) {
300         cacheIterator = presentationAttributeCache().add(cacheHash, nullptr).iterator;
301         if (cacheIterator->second && cacheIterator->second->key != cacheKey)
302             cacheHash = 0;
303     } else
304         cacheIterator = presentationAttributeCache().end();
305
306     RefPtr<StylePropertySet> style;
307     if (cacheHash && cacheIterator->second) {
308         style = cacheIterator->second->value;
309         presentationAttributeCacheCleaner().didHitPresentationAttributeCache();
310     } else {
311         style = StylePropertySet::create(isSVGElement() ? SVGAttributeMode : CSSQuirksMode);
312         unsigned size = attributeCount();
313         for (unsigned i = 0; i < size; ++i) {
314             Attribute* attribute = attributeItem(i);
315             collectStyleForAttribute(*attribute, style.get());
316         }
317     }
318     clearAttributeStyleDirty();
319
320     attributeData()->setAttributeStyle(style->isEmpty() ? 0 : style);
321
322     if (!cacheHash || cacheIterator->second)
323         return;
324
325     OwnPtr<PresentationAttributeCacheEntry> newEntry = adoptPtr(new PresentationAttributeCacheEntry);
326     newEntry->key = cacheKey;
327     newEntry->value = style.release();
328
329     static const int presentationAttributeCacheMaximumSize = 4096;
330     if (presentationAttributeCache().size() > presentationAttributeCacheMaximumSize) {
331         // Start building from scratch if the cache ever gets big.
332         presentationAttributeCache().clear();
333         presentationAttributeCache().set(cacheHash, newEntry.release());
334     } else
335         cacheIterator->second = newEntry.release();
336 }
337
338 void StyledElement::addPropertyToAttributeStyle(StylePropertySet* style, CSSPropertyID propertyID, int identifier)
339 {
340     style->setProperty(propertyID, cssValuePool().createIdentifierValue(identifier));
341 }
342
343 void StyledElement::addPropertyToAttributeStyle(StylePropertySet* style, CSSPropertyID propertyID, double value, CSSPrimitiveValue::UnitTypes unit)
344 {
345     style->setProperty(propertyID, cssValuePool().createValue(value, unit));
346 }
347     
348 void StyledElement::addPropertyToAttributeStyle(StylePropertySet* style, CSSPropertyID propertyID, const String& value)
349 {
350     style->setProperty(propertyID, value, false, document()->elementSheet()->contents());
351 }
352
353 }