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 "StorageAreaImpl.h" 28 29#include "Document.h" 30#include "ExceptionCode.h" 31#include "Frame.h" 32#include "Page.h" 33#include "SchemeRegistry.h" 34#include "SecurityOrigin.h" 35#include "Settings.h" 36#include "StorageAreaSync.h" 37#include "StorageEventDispatcher.h" 38#include "StorageMap.h" 39#include "StorageSyncManager.h" 40#include "StorageTracker.h" 41#include <wtf/MainThread.h> 42 43namespace WebCore { 44 45StorageAreaImpl::~StorageAreaImpl() 46{ 47 ASSERT(isMainThread()); 48} 49 50inline StorageAreaImpl::StorageAreaImpl(StorageType storageType, PassRefPtr<SecurityOrigin> origin, PassRefPtr<StorageSyncManager> syncManager, unsigned quota) 51 : m_storageType(storageType) 52 , m_securityOrigin(origin) 53 , m_storageMap(StorageMap::create(quota)) 54 , m_storageSyncManager(syncManager) 55#ifndef NDEBUG 56 , m_isShutdown(false) 57#endif 58 , m_accessCount(0) 59 , m_closeDatabaseTimer(this, &StorageAreaImpl::closeDatabaseTimerFired) 60{ 61 ASSERT(isMainThread()); 62 ASSERT(m_securityOrigin); 63 ASSERT(m_storageMap); 64 65 // Accessing the shared global StorageTracker when a StorageArea is created 66 // ensures that the tracker is properly initialized before anyone actually needs to use it. 67 StorageTracker::tracker(); 68} 69 70PassRefPtr<StorageAreaImpl> StorageAreaImpl::create(StorageType storageType, PassRefPtr<SecurityOrigin> origin, PassRefPtr<StorageSyncManager> syncManager, unsigned quota) 71{ 72 RefPtr<StorageAreaImpl> area = adoptRef(new StorageAreaImpl(storageType, origin, syncManager, quota)); 73 74 // FIXME: If there's no backing storage for LocalStorage, the default WebKit behavior should be that of private browsing, 75 // not silently ignoring it. https://bugs.webkit.org/show_bug.cgi?id=25894 76 if (area->m_storageSyncManager) { 77 area->m_storageAreaSync = StorageAreaSync::create(area->m_storageSyncManager, area.get(), area->m_securityOrigin->databaseIdentifier()); 78 ASSERT(area->m_storageAreaSync); 79 } 80 81 return area.release(); 82} 83 84PassRefPtr<StorageAreaImpl> StorageAreaImpl::copy() 85{ 86 ASSERT(!m_isShutdown); 87 return adoptRef(new StorageAreaImpl(this)); 88} 89 90StorageAreaImpl::StorageAreaImpl(StorageAreaImpl* area) 91 : m_storageType(area->m_storageType) 92 , m_securityOrigin(area->m_securityOrigin) 93 , m_storageMap(area->m_storageMap) 94 , m_storageSyncManager(area->m_storageSyncManager) 95#ifndef NDEBUG 96 , m_isShutdown(area->m_isShutdown) 97#endif 98 , m_accessCount(0) 99 , m_closeDatabaseTimer(this, &StorageAreaImpl::closeDatabaseTimerFired) 100{ 101 ASSERT(isMainThread()); 102 ASSERT(m_securityOrigin); 103 ASSERT(m_storageMap); 104 ASSERT(!m_isShutdown); 105} 106 107bool StorageAreaImpl::canAccessStorage(Frame* frame) 108{ 109 return frame && frame->page(); 110} 111 112StorageType StorageAreaImpl::storageType() const 113{ 114 return m_storageType; 115} 116 117unsigned StorageAreaImpl::length() 118{ 119 ASSERT(!m_isShutdown); 120 blockUntilImportComplete(); 121 122 return m_storageMap->length(); 123} 124 125String StorageAreaImpl::key(unsigned index) 126{ 127 ASSERT(!m_isShutdown); 128 blockUntilImportComplete(); 129 130 return m_storageMap->key(index); 131} 132 133String StorageAreaImpl::item(const String& key) 134{ 135 ASSERT(!m_isShutdown); 136 blockUntilImportComplete(); 137 138 return m_storageMap->getItem(key); 139} 140 141void StorageAreaImpl::setItem(Frame* sourceFrame, const String& key, const String& value, bool& quotaException) 142{ 143 ASSERT(!m_isShutdown); 144 ASSERT(!value.isNull()); 145 blockUntilImportComplete(); 146 147 String oldValue; 148 RefPtr<StorageMap> newMap = m_storageMap->setItem(key, value, oldValue, quotaException); 149 if (newMap) 150 m_storageMap = newMap.release(); 151 152 if (quotaException) 153 return; 154 155 if (oldValue == value) 156 return; 157 158 if (m_storageAreaSync) 159 m_storageAreaSync->scheduleItemForSync(key, value); 160 161 dispatchStorageEvent(key, oldValue, value, sourceFrame); 162} 163 164void StorageAreaImpl::removeItem(Frame* sourceFrame, const String& key) 165{ 166 ASSERT(!m_isShutdown); 167 blockUntilImportComplete(); 168 169 String oldValue; 170 RefPtr<StorageMap> newMap = m_storageMap->removeItem(key, oldValue); 171 if (newMap) 172 m_storageMap = newMap.release(); 173 174 if (oldValue.isNull()) 175 return; 176 177 if (m_storageAreaSync) 178 m_storageAreaSync->scheduleItemForSync(key, String()); 179 180 dispatchStorageEvent(key, oldValue, String(), sourceFrame); 181} 182 183void StorageAreaImpl::clear(Frame* sourceFrame) 184{ 185 ASSERT(!m_isShutdown); 186 blockUntilImportComplete(); 187 188 if (!m_storageMap->length()) 189 return; 190 191 unsigned quota = m_storageMap->quota(); 192 m_storageMap = StorageMap::create(quota); 193 194 if (m_storageAreaSync) 195 m_storageAreaSync->scheduleClear(); 196 197 dispatchStorageEvent(String(), String(), String(), sourceFrame); 198} 199 200bool StorageAreaImpl::contains(const String& key) 201{ 202 ASSERT(!m_isShutdown); 203 blockUntilImportComplete(); 204 205 return m_storageMap->contains(key); 206} 207 208void StorageAreaImpl::importItems(const HashMap<String, String>& items) 209{ 210 ASSERT(!m_isShutdown); 211 212 m_storageMap->importItems(items); 213} 214 215void StorageAreaImpl::close() 216{ 217 if (m_storageAreaSync) 218 m_storageAreaSync->scheduleFinalSync(); 219 220#ifndef NDEBUG 221 m_isShutdown = true; 222#endif 223} 224 225void StorageAreaImpl::clearForOriginDeletion() 226{ 227 ASSERT(!m_isShutdown); 228 blockUntilImportComplete(); 229 230 if (m_storageMap->length()) { 231 unsigned quota = m_storageMap->quota(); 232 m_storageMap = StorageMap::create(quota); 233 } 234 235 if (m_storageAreaSync) { 236 m_storageAreaSync->scheduleClear(); 237 m_storageAreaSync->scheduleCloseDatabase(); 238 } 239} 240 241void StorageAreaImpl::sync() 242{ 243 ASSERT(!m_isShutdown); 244 blockUntilImportComplete(); 245 246 if (m_storageAreaSync) 247 m_storageAreaSync->scheduleSync(); 248} 249 250void StorageAreaImpl::blockUntilImportComplete() const 251{ 252 if (m_storageAreaSync) 253 m_storageAreaSync->blockUntilImportComplete(); 254} 255 256size_t StorageAreaImpl::memoryBytesUsedByCache() 257{ 258 return 0; 259} 260 261void StorageAreaImpl::incrementAccessCount() 262{ 263 m_accessCount++; 264 265 if (m_closeDatabaseTimer.isActive()) 266 m_closeDatabaseTimer.stop(); 267} 268 269void StorageAreaImpl::decrementAccessCount() 270{ 271 ASSERT(m_accessCount); 272 --m_accessCount; 273 274 if (!m_accessCount) { 275 if (m_closeDatabaseTimer.isActive()) 276 m_closeDatabaseTimer.stop(); 277 m_closeDatabaseTimer.startOneShot(StorageTracker::tracker().storageDatabaseIdleInterval()); 278 } 279} 280 281void StorageAreaImpl::closeDatabaseTimerFired(Timer<StorageAreaImpl> *) 282{ 283 blockUntilImportComplete(); 284 if (m_storageAreaSync) 285 m_storageAreaSync->scheduleCloseDatabase(); 286} 287 288void StorageAreaImpl::closeDatabaseIfIdle() 289{ 290 if (m_closeDatabaseTimer.isActive()) { 291 ASSERT(!m_accessCount); 292 m_closeDatabaseTimer.stop(); 293 294 closeDatabaseTimerFired(&m_closeDatabaseTimer); 295 } 296} 297 298void StorageAreaImpl::dispatchStorageEvent(const String& key, const String& oldValue, const String& newValue, Frame* sourceFrame) 299{ 300 if (m_storageType == LocalStorage) 301 StorageEventDispatcher::dispatchLocalStorageEvents(key, oldValue, newValue, m_securityOrigin.get(), sourceFrame); 302 else 303 StorageEventDispatcher::dispatchSessionStorageEvents(key, oldValue, newValue, m_securityOrigin.get(), sourceFrame); 304} 305 306} // namespace WebCore 307