2 * Copyright (C) 2013 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include "core/dom/PresentationAttributeStyle.h"
34 #include "core/css/StylePropertySet.h"
35 #include "core/dom/Attribute.h"
36 #include "core/dom/Element.h"
37 #include "core/html/HTMLInputElement.h"
38 #include "platform/Timer.h"
39 #include "wtf/HashFunctions.h"
40 #include "wtf/HashMap.h"
41 #include "wtf/text/CString.h"
45 using namespace HTMLNames
;
47 struct PresentationAttributeCacheKey
{
48 PresentationAttributeCacheKey() : tagName(nullptr) { }
50 Vector
<std::pair
<StringImpl
*, AtomicString
>, 3> attributesAndValues
;
53 static bool operator!=(const PresentationAttributeCacheKey
& a
, const PresentationAttributeCacheKey
& b
)
55 if (a
.tagName
!= b
.tagName
)
57 return a
.attributesAndValues
!= b
.attributesAndValues
;
60 struct PresentationAttributeCacheEntry final
: public NoBaseWillBeGarbageCollectedFinalized
<PresentationAttributeCacheEntry
> {
61 WTF_MAKE_FAST_ALLOCATED_WILL_BE_REMOVED(PresentationAttributeCacheEntry
);
63 DEFINE_INLINE_TRACE() { visitor
->trace(value
); }
65 PresentationAttributeCacheKey key
;
66 RefPtrWillBeMember
<StylePropertySet
> value
;
69 using PresentationAttributeCache
= WillBeHeapHashMap
<unsigned, OwnPtrWillBeMember
<PresentationAttributeCacheEntry
>, AlreadyHashed
>;
70 static PresentationAttributeCache
& presentationAttributeCache()
72 DEFINE_STATIC_LOCAL(OwnPtrWillBePersistent
<PresentationAttributeCache
>, cache
, (adoptPtrWillBeNoop(new PresentationAttributeCache())));
76 class PresentationAttributeCacheCleaner
{
77 WTF_MAKE_NONCOPYABLE(PresentationAttributeCacheCleaner
); WTF_MAKE_FAST_ALLOCATED(PresentationAttributeCacheCleaner
);
79 PresentationAttributeCacheCleaner()
81 , m_cleanTimer(this, &PresentationAttributeCacheCleaner::cleanCache
)
85 void didHitPresentationAttributeCache()
87 if (presentationAttributeCache().size() < minimumPresentationAttributeCacheSizeForCleaning
)
92 if (!m_cleanTimer
.isActive())
93 m_cleanTimer
.startOneShot(presentationAttributeCacheCleanTimeInSeconds
, FROM_HERE
);
97 static const unsigned presentationAttributeCacheCleanTimeInSeconds
= 60;
98 static const unsigned minimumPresentationAttributeCacheSizeForCleaning
= 100;
99 static const unsigned minimumPresentationAttributeCacheHitCountPerMinute
= (100 * presentationAttributeCacheCleanTimeInSeconds
) / 60;
101 void cleanCache(Timer
<PresentationAttributeCacheCleaner
>* timer
)
103 ASSERT_UNUSED(timer
, timer
== &m_cleanTimer
);
104 unsigned hitCount
= m_hitCount
;
106 if (hitCount
> minimumPresentationAttributeCacheHitCountPerMinute
)
108 presentationAttributeCache().clear();
112 Timer
<PresentationAttributeCacheCleaner
> m_cleanTimer
;
115 static bool attributeNameSort(const pair
<StringImpl
*, AtomicString
>& p1
, const pair
<StringImpl
*, AtomicString
>& p2
)
117 // Sort based on the attribute name pointers. It doesn't matter what the order is as long as it is always the same.
118 return p1
.first
< p2
.first
;
121 static void makePresentationAttributeCacheKey(Element
& element
, PresentationAttributeCacheKey
& result
)
123 // FIXME: Enable for SVG.
124 if (!element
.isHTMLElement())
126 // Interpretation of the size attributes on <input> depends on the type attribute.
127 if (isHTMLInputElement(element
))
129 AttributeCollection attributes
= element
.attributesWithoutUpdate();
130 for (const Attribute
& attr
: attributes
) {
131 if (!element
.isPresentationAttribute(attr
.name()))
133 if (!attr
.namespaceURI().isNull())
135 // FIXME: Background URL may depend on the base URL and can't be shared. Disallow caching.
136 if (attr
.name() == backgroundAttr
)
138 result
.attributesAndValues
.append(std::make_pair(attr
.localName().impl(), attr
.value()));
140 if (result
.attributesAndValues
.isEmpty())
142 // Attribute order doesn't matter. Sort for easy equality comparison.
143 std::sort(result
.attributesAndValues
.begin(), result
.attributesAndValues
.end(), attributeNameSort
);
144 // The cache key is non-null when the tagName is set.
145 result
.tagName
= element
.localName().impl();
148 static unsigned computePresentationAttributeCacheHash(const PresentationAttributeCacheKey
& key
)
152 ASSERT(key
.attributesAndValues
.size());
153 unsigned attributeHash
= StringHasher::hashMemory(key
.attributesAndValues
.data(), key
.attributesAndValues
.size() * sizeof(key
.attributesAndValues
[0]));
154 return WTF::pairIntHash(key
.tagName
->existingHash(), attributeHash
);
157 PassRefPtrWillBeRawPtr
<StylePropertySet
> computePresentationAttributeStyle(Element
& element
)
159 DEFINE_STATIC_LOCAL(PresentationAttributeCacheCleaner
, cacheCleaner
, ());
161 ASSERT(element
.isStyledElement());
163 PresentationAttributeCacheKey cacheKey
;
164 makePresentationAttributeCacheKey(element
, cacheKey
);
166 unsigned cacheHash
= computePresentationAttributeCacheHash(cacheKey
);
168 PresentationAttributeCache::ValueType
* cacheValue
;
170 cacheValue
= presentationAttributeCache().add(cacheHash
, nullptr).storedValue
;
171 if (cacheValue
->value
&& cacheValue
->value
->key
!= cacheKey
)
174 cacheValue
= nullptr;
177 RefPtrWillBeRawPtr
<StylePropertySet
> style
= nullptr;
178 if (cacheHash
&& cacheValue
->value
) {
179 style
= cacheValue
->value
->value
;
180 cacheCleaner
.didHitPresentationAttributeCache();
182 style
= MutableStylePropertySet::create(element
.isSVGElement() ? SVGAttributeMode
: HTMLAttributeMode
);
183 AttributeCollection attributes
= element
.attributesWithoutUpdate();
184 for (const Attribute
& attr
: attributes
)
185 element
.collectStyleForPresentationAttribute(attr
.name(), attr
.value(), toMutableStylePropertySet(style
));
188 if (!cacheHash
|| cacheValue
->value
)
189 return style
.release();
191 OwnPtrWillBeRawPtr
<PresentationAttributeCacheEntry
> newEntry
= adoptPtrWillBeNoop(new PresentationAttributeCacheEntry
);
192 newEntry
->key
= cacheKey
;
193 newEntry
->value
= style
;
195 static const unsigned presentationAttributeCacheMaximumSize
= 4096;
196 if (presentationAttributeCache().size() > presentationAttributeCacheMaximumSize
) {
197 // FIXME: Discarding the entire cache when it gets too big is probably bad
198 // since it creates a perf "cliff". Perhaps we should use an LRU?
199 presentationAttributeCache().clear();
200 presentationAttributeCache().set(cacheHash
, newEntry
.release());
202 cacheValue
->value
= newEntry
.release();
205 return style
.release();