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