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