1/* 2 * Copyright (C) 2008 Apple 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 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "StorageMap.h" 28 29#if ENABLE(DOM_STORAGE) 30 31namespace WebCore { 32 33PassRefPtr<StorageMap> StorageMap::create(unsigned quota) 34{ 35 return adoptRef(new StorageMap(quota)); 36} 37 38StorageMap::StorageMap(unsigned quota) 39 : m_iterator(m_map.end()) 40 , m_iteratorIndex(UINT_MAX) 41 , m_quotaSize(quota) // quota measured in bytes 42 , m_currentLength(0) 43{ 44} 45 46PassRefPtr<StorageMap> StorageMap::copy() 47{ 48 RefPtr<StorageMap> newMap = create(m_quotaSize); 49 newMap->m_map = m_map; 50 newMap->m_currentLength = m_currentLength; 51 return newMap.release(); 52} 53 54void StorageMap::invalidateIterator() 55{ 56 m_iterator = m_map.end(); 57 m_iteratorIndex = UINT_MAX; 58} 59 60void StorageMap::setIteratorToIndex(unsigned index) 61{ 62 // FIXME: Once we have bidirectional iterators for HashMap we can be more intelligent about this. 63 // The requested index will be closest to begin(), our current iterator, or end(), and we 64 // can take the shortest route. 65 // Until that mechanism is available, we'll always increment our iterator from begin() or current. 66 67 if (m_iteratorIndex == index) 68 return; 69 70 if (index < m_iteratorIndex) { 71 m_iteratorIndex = 0; 72 m_iterator = m_map.begin(); 73 ASSERT(m_iterator != m_map.end()); 74 } 75 76 while (m_iteratorIndex < index) { 77 ++m_iteratorIndex; 78 ++m_iterator; 79 ASSERT(m_iterator != m_map.end()); 80 } 81} 82 83unsigned StorageMap::length() const 84{ 85 return m_map.size(); 86} 87 88String StorageMap::key(unsigned index) 89{ 90 if (index >= length()) 91 return String(); 92 93 setIteratorToIndex(index); 94 return m_iterator->first; 95} 96 97String StorageMap::getItem(const String& key) const 98{ 99 return m_map.get(key); 100} 101 102PassRefPtr<StorageMap> StorageMap::setItem(const String& key, const String& value, String& oldValue, bool& quotaException) 103{ 104 ASSERT(!value.isNull()); 105 quotaException = false; 106 107 // Implement copy-on-write semantics here. We're guaranteed that the only refs of StorageMaps belong to Storage objects 108 // so if more than one Storage object refs this map, copy it before mutating it. 109 if (refCount() > 1) { 110 RefPtr<StorageMap> newStorageMap = copy(); 111 newStorageMap->setItem(key, value, oldValue, quotaException); 112 return newStorageMap.release(); 113 } 114 115 // Quota tracking. This is done in a couple of steps to keep the overflow tracking simple. 116 unsigned newLength = m_currentLength; 117 bool overflow = newLength + value.length() < newLength; 118 newLength += value.length(); 119 120 oldValue = m_map.get(key); 121 overflow |= newLength - oldValue.length() > newLength; 122 newLength -= oldValue.length(); 123 124 unsigned adjustedKeyLength = oldValue.isNull() ? key.length() : 0; 125 overflow |= newLength + adjustedKeyLength < newLength; 126 newLength += adjustedKeyLength; 127 128 ASSERT(!overflow); // Overflow is bad...even if quotas are off. 129 bool overQuota = newLength > m_quotaSize / sizeof(UChar); 130 if (m_quotaSize != noQuota && (overflow || overQuota)) { 131 quotaException = true; 132 return 0; 133 } 134 m_currentLength = newLength; 135 136 pair<HashMap<String, String>::iterator, bool> addResult = m_map.add(key, value); 137 if (!addResult.second) 138 addResult.first->second = value; 139 140 invalidateIterator(); 141 142 return 0; 143} 144 145PassRefPtr<StorageMap> StorageMap::removeItem(const String& key, String& oldValue) 146{ 147 // Implement copy-on-write semantics here. We're guaranteed that the only refs of StorageMaps belong to Storage objects 148 // so if more than one Storage object refs this map, copy it before mutating it. 149 if (refCount() > 1) { 150 RefPtr<StorageMap> newStorage = copy(); 151 newStorage->removeItem(key, oldValue); 152 return newStorage.release(); 153 } 154 155 oldValue = m_map.take(key); 156 if (!oldValue.isNull()) { 157 invalidateIterator(); 158 ASSERT(m_currentLength - key.length() <= m_currentLength); 159 m_currentLength -= key.length(); 160 } 161 ASSERT(m_currentLength - oldValue.length() <= m_currentLength); 162 m_currentLength -= oldValue.length(); 163 164 return 0; 165} 166 167bool StorageMap::contains(const String& key) const 168{ 169 return m_map.contains(key); 170} 171 172void StorageMap::importItem(const String& key, const String& value) 173{ 174 // Be sure to copy the keys/values as items imported on a background thread are destined 175 // to cross a thread boundary 176 pair<HashMap<String, String>::iterator, bool> result = m_map.add(key.threadsafeCopy(), value.threadsafeCopy()); 177 ASSERT(result.second); // True if the key didn't exist previously. 178 179 ASSERT(m_currentLength + key.length() >= m_currentLength); 180 m_currentLength += key.length(); 181 ASSERT(m_currentLength + value.length() >= m_currentLength); 182 m_currentLength += value.length(); 183} 184 185} 186 187#endif // ENABLE(DOM_STORAGE) 188