1 /* 2 * Copyright (C) 2013 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 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 13 * distribution. 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. 17 * 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. 29 */ 30 31 #include "config.h" 32 #include "core/dom/PresentationAttributeStyle.h" 33 34 #include "HTMLNames.h" 35 #include "core/css/StylePropertySet.h" 36 #include "core/dom/Attribute.h" 37 #include "core/dom/Element.h" 38 #include "wtf/HashFunctions.h" 39 #include "wtf/HashMap.h" 40 #include "wtf/text/CString.h" 41 42 namespace WebCore { 43 44 using namespace HTMLNames; 45 46 struct PresentationAttributeCacheKey { 47 PresentationAttributeCacheKey() : tagName(0) { } 48 StringImpl* tagName; 49 Vector<std::pair<StringImpl*, AtomicString>, 3> attributesAndValues; 50 }; 51 52 static bool operator!=(const PresentationAttributeCacheKey& a, const PresentationAttributeCacheKey& b) 53 { 54 if (a.tagName != b.tagName) 55 return true; 56 return a.attributesAndValues != b.attributesAndValues; 57 } 58 59 struct PresentationAttributeCacheEntry { 60 WTF_MAKE_FAST_ALLOCATED; 61 public: 62 PresentationAttributeCacheKey key; 63 RefPtr<StylePropertySet> value; 64 }; 65 66 typedef HashMap<unsigned, OwnPtr<PresentationAttributeCacheEntry>, AlreadyHashed> PresentationAttributeCache; 67 static PresentationAttributeCache& presentationAttributeCache() 68 { 69 DEFINE_STATIC_LOCAL(PresentationAttributeCache, cache, ()); 70 return cache; 71 } 72 73 class PresentationAttributeCacheCleaner { 74 WTF_MAKE_NONCOPYABLE(PresentationAttributeCacheCleaner); WTF_MAKE_FAST_ALLOCATED; 75 public: 76 PresentationAttributeCacheCleaner() 77 : m_hitCount(0) 78 , m_cleanTimer(this, &PresentationAttributeCacheCleaner::cleanCache) 79 { 80 } 81 82 void didHitPresentationAttributeCache() 83 { 84 if (presentationAttributeCache().size() < minimumPresentationAttributeCacheSizeForCleaning) 85 return; 86 87 m_hitCount++; 88 89 if (!m_cleanTimer.isActive()) 90 m_cleanTimer.startOneShot(presentationAttributeCacheCleanTimeInSeconds); 91 } 92 93 private: 94 static const unsigned presentationAttributeCacheCleanTimeInSeconds = 60; 95 static const unsigned minimumPresentationAttributeCacheSizeForCleaning = 100; 96 static const unsigned minimumPresentationAttributeCacheHitCountPerMinute = (100 * presentationAttributeCacheCleanTimeInSeconds) / 60; 97 98 void cleanCache(Timer<PresentationAttributeCacheCleaner>* timer) 99 { 100 ASSERT_UNUSED(timer, timer == &m_cleanTimer); 101 unsigned hitCount = m_hitCount; 102 m_hitCount = 0; 103 if (hitCount > minimumPresentationAttributeCacheHitCountPerMinute) 104 return; 105 presentationAttributeCache().clear(); 106 } 107 108 unsigned m_hitCount; 109 Timer<PresentationAttributeCacheCleaner> m_cleanTimer; 110 }; 111 112 static bool attributeNameSort(const pair<StringImpl*, AtomicString>& p1, const pair<StringImpl*, AtomicString>& p2) 113 { 114 // Sort based on the attribute name pointers. It doesn't matter what the order is as long as it is always the same. 115 return p1.first < p2.first; 116 } 117 118 static void makePresentationAttributeCacheKey(Element& element, PresentationAttributeCacheKey& result) 119 { 120 // FIXME: Enable for SVG. 121 if (!element.isHTMLElement()) 122 return; 123 // Interpretation of the size attributes on <input> depends on the type attribute. 124 if (element.hasTagName(inputTag)) 125 return; 126 unsigned size = element.attributeCount(); 127 for (unsigned i = 0; i < size; ++i) { 128 const Attribute* attribute = element.attributeItem(i); 129 if (!element.isPresentationAttribute(attribute->name())) 130 continue; 131 if (!attribute->namespaceURI().isNull()) 132 return; 133 // FIXME: Background URL may depend on the base URL and can't be shared. Disallow caching. 134 if (attribute->name() == backgroundAttr) 135 return; 136 result.attributesAndValues.append(std::make_pair(attribute->localName().impl(), attribute->value())); 137 } 138 if (result.attributesAndValues.isEmpty()) 139 return; 140 // Attribute order doesn't matter. Sort for easy equality comparison. 141 std::sort(result.attributesAndValues.begin(), result.attributesAndValues.end(), attributeNameSort); 142 // The cache key is non-null when the tagName is set. 143 result.tagName = element.localName().impl(); 144 } 145 146 static unsigned computePresentationAttributeCacheHash(const PresentationAttributeCacheKey& key) 147 { 148 if (!key.tagName) 149 return 0; 150 ASSERT(key.attributesAndValues.size()); 151 unsigned attributeHash = StringHasher::hashMemory(key.attributesAndValues.data(), key.attributesAndValues.size() * sizeof(key.attributesAndValues[0])); 152 return WTF::pairIntHash(key.tagName->existingHash(), attributeHash); 153 } 154 155 PassRefPtr<StylePropertySet> computePresentationAttributeStyle(Element& element) 156 { 157 DEFINE_STATIC_LOCAL(PresentationAttributeCacheCleaner, cacheCleaner, ()); 158 159 ASSERT(element.isStyledElement()); 160 161 PresentationAttributeCacheKey cacheKey; 162 makePresentationAttributeCacheKey(element, cacheKey); 163 164 unsigned cacheHash = computePresentationAttributeCacheHash(cacheKey); 165 166 PresentationAttributeCache::iterator cacheIterator; 167 if (cacheHash) { 168 cacheIterator = presentationAttributeCache().add(cacheHash, nullptr).iterator; 169 if (cacheIterator->value && cacheIterator->value->key != cacheKey) 170 cacheHash = 0; 171 } else { 172 cacheIterator = presentationAttributeCache().end(); 173 } 174 175 RefPtr<StylePropertySet> style; 176 if (cacheHash && cacheIterator->value) { 177 style = cacheIterator->value->value; 178 cacheCleaner.didHitPresentationAttributeCache(); 179 } else { 180 style = MutableStylePropertySet::create(element.isSVGElement() ? SVGAttributeMode : HTMLAttributeMode); 181 unsigned size = element.attributeCount(); 182 for (unsigned i = 0; i < size; ++i) { 183 const Attribute* attribute = element.attributeItem(i); 184 element.collectStyleForPresentationAttribute(attribute->name(), attribute->value(), toMutableStylePropertySet(style)); 185 } 186 } 187 188 if (!cacheHash || cacheIterator->value) 189 return style.release(); 190 191 OwnPtr<PresentationAttributeCacheEntry> newEntry = adoptPtr(new PresentationAttributeCacheEntry); 192 newEntry->key = cacheKey; 193 newEntry->value = style; 194 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()); 201 } else { 202 cacheIterator->value = newEntry.release(); 203 } 204 205 return style.release(); 206 } 207 208 } // namespace WebCore 209