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 "StorageNamespaceImpl.h"
28
29#include "GroupSettings.h"
30#include "Page.h"
31#include "PageGroup.h"
32#include "SecurityOriginHash.h"
33#include "Settings.h"
34#include "StorageAreaImpl.h"
35#include "StorageMap.h"
36#include "StorageSyncManager.h"
37#include "StorageTracker.h"
38#include <wtf/MainThread.h>
39#include <wtf/StdLibExtras.h>
40#include <wtf/text/StringHash.h>
41
42namespace WebCore {
43
44typedef HashMap<String, StorageNamespace*> LocalStorageNamespaceMap;
45
46static LocalStorageNamespaceMap& localStorageNamespaceMap()
47{
48    DEPRECATED_DEFINE_STATIC_LOCAL(LocalStorageNamespaceMap, localStorageNamespaceMap, ());
49    return localStorageNamespaceMap;
50}
51
52PassRefPtr<StorageNamespace> StorageNamespaceImpl::localStorageNamespace(PageGroup* pageGroup)
53{
54    // Need a page in this page group to query the settings for the local storage database path.
55    // Having these parameters attached to the page settings is unfortunate since these settings are
56    // not per-page (and, in fact, we simply grab the settings from some page at random), but
57    // at this point we're stuck with it.
58    Page* page = *pageGroup->pages().begin();
59    const String& path = page->settings().localStorageDatabasePath();
60    unsigned quota = pageGroup->groupSettings().localStorageQuotaBytes();
61    const String lookupPath = path.isNull() ? emptyString() : path;
62
63    LocalStorageNamespaceMap::AddResult result = localStorageNamespaceMap().add(lookupPath, nullptr);
64    if (!result.isNewEntry)
65        return result.iterator->value;
66
67    RefPtr<StorageNamespace> storageNamespace = adoptRef(new StorageNamespaceImpl(LocalStorage, lookupPath, quota));
68
69    result.iterator->value = storageNamespace.get();
70    return storageNamespace.release();
71}
72
73PassRefPtr<StorageNamespace> StorageNamespaceImpl::sessionStorageNamespace(Page* page)
74{
75    return adoptRef(new StorageNamespaceImpl(SessionStorage, String(), page->settings().sessionStorageQuota()));
76}
77
78PassRefPtr<StorageNamespace> StorageNamespaceImpl::transientLocalStorageNamespace(PageGroup* pageGroup, SecurityOrigin*)
79{
80    // FIXME: A smarter implementation would create a special namespace type instead of just piggy-backing off
81    // SessionStorageNamespace here.
82    return StorageNamespaceImpl::sessionStorageNamespace(*pageGroup->pages().begin());
83}
84
85StorageNamespaceImpl::StorageNamespaceImpl(StorageType storageType, const String& path, unsigned quota)
86    : m_storageType(storageType)
87    , m_path(path.isolatedCopy())
88    , m_syncManager(0)
89    , m_quota(quota)
90    , m_isShutdown(false)
91{
92    if (m_storageType == LocalStorage && !m_path.isEmpty())
93        m_syncManager = StorageSyncManager::create(m_path);
94}
95
96StorageNamespaceImpl::~StorageNamespaceImpl()
97{
98    ASSERT(isMainThread());
99
100    if (m_storageType == LocalStorage) {
101        ASSERT(localStorageNamespaceMap().get(m_path) == this);
102        localStorageNamespaceMap().remove(m_path);
103    }
104
105    if (!m_isShutdown)
106        close();
107}
108
109PassRefPtr<StorageNamespace> StorageNamespaceImpl::copy(Page*)
110{
111    ASSERT(isMainThread());
112    ASSERT(!m_isShutdown);
113    ASSERT(m_storageType == SessionStorage);
114
115    RefPtr<StorageNamespaceImpl> newNamespace = adoptRef(new StorageNamespaceImpl(m_storageType, m_path, m_quota));
116
117    StorageAreaMap::iterator end = m_storageAreaMap.end();
118    for (StorageAreaMap::iterator i = m_storageAreaMap.begin(); i != end; ++i)
119        newNamespace->m_storageAreaMap.set(i->key, i->value->copy());
120    return newNamespace.release();
121}
122
123PassRefPtr<StorageArea> StorageNamespaceImpl::storageArea(PassRefPtr<SecurityOrigin> prpOrigin)
124{
125    ASSERT(isMainThread());
126    ASSERT(!m_isShutdown);
127
128    RefPtr<SecurityOrigin> origin = prpOrigin;
129    RefPtr<StorageAreaImpl> storageArea;
130    if ((storageArea = m_storageAreaMap.get(origin)))
131        return storageArea.release();
132
133    storageArea = StorageAreaImpl::create(m_storageType, origin, m_syncManager, m_quota);
134    m_storageAreaMap.set(origin.release(), storageArea);
135    return storageArea.release();
136}
137
138void StorageNamespaceImpl::close()
139{
140    ASSERT(isMainThread());
141
142    if (m_isShutdown)
143        return;
144
145    // If we're session storage, we shouldn't need to do any work here.
146    if (m_storageType == SessionStorage) {
147        ASSERT(!m_syncManager);
148        return;
149    }
150
151    StorageAreaMap::iterator end = m_storageAreaMap.end();
152    for (StorageAreaMap::iterator it = m_storageAreaMap.begin(); it != end; ++it)
153        it->value->close();
154
155    if (m_syncManager)
156        m_syncManager->close();
157
158    m_isShutdown = true;
159}
160
161void StorageNamespaceImpl::clearOriginForDeletion(SecurityOrigin* origin)
162{
163    ASSERT(isMainThread());
164
165    RefPtr<StorageAreaImpl> storageArea = m_storageAreaMap.get(origin);
166    if (storageArea)
167        storageArea->clearForOriginDeletion();
168}
169
170void StorageNamespaceImpl::clearAllOriginsForDeletion()
171{
172    ASSERT(isMainThread());
173
174    StorageAreaMap::iterator end = m_storageAreaMap.end();
175    for (StorageAreaMap::iterator it = m_storageAreaMap.begin(); it != end; ++it)
176        it->value->clearForOriginDeletion();
177}
178
179void StorageNamespaceImpl::sync()
180{
181    ASSERT(isMainThread());
182    StorageAreaMap::iterator end = m_storageAreaMap.end();
183    for (StorageAreaMap::iterator it = m_storageAreaMap.begin(); it != end; ++it)
184        it->value->sync();
185}
186
187void StorageNamespaceImpl::closeIdleLocalStorageDatabases()
188{
189    ASSERT(isMainThread());
190    StorageAreaMap::iterator end = m_storageAreaMap.end();
191    for (StorageAreaMap::iterator it = m_storageAreaMap.begin(); it != end; ++it)
192        it->value->closeDatabaseIfIdle();
193}
194
195} // namespace WebCore
196