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#if ENABLE(DOM_STORAGE)
30
31#include "SecurityOriginHash.h"
32#include "StorageAreaImpl.h"
33#include "StorageMap.h"
34#include "StorageSyncManager.h"
35#include "StorageTracker.h"
36#include <wtf/StdLibExtras.h>
37#include <wtf/text/StringHash.h>
38
39#ifdef ANDROID
40#include "Page.h"
41#endif
42
43namespace WebCore {
44
45typedef HashMap<String, StorageNamespace*> LocalStorageNamespaceMap;
46
47static LocalStorageNamespaceMap& localStorageNamespaceMap()
48{
49    DEFINE_STATIC_LOCAL(LocalStorageNamespaceMap, localStorageNamespaceMap, ());
50    return localStorageNamespaceMap;
51}
52
53PassRefPtr<StorageNamespace> StorageNamespaceImpl::localStorageNamespace(const String& path, unsigned quota)
54{
55    const String lookupPath = path.isNull() ? String("") : path;
56    LocalStorageNamespaceMap::iterator it = localStorageNamespaceMap().find(lookupPath);
57    if (it == localStorageNamespaceMap().end()) {
58        RefPtr<StorageNamespace> storageNamespace = adoptRef(new StorageNamespaceImpl(LocalStorage, lookupPath, quota));
59        localStorageNamespaceMap().set(lookupPath, storageNamespace.get());
60        return storageNamespace.release();
61    }
62
63    return it->second;
64}
65
66PassRefPtr<StorageNamespace> StorageNamespaceImpl::sessionStorageNamespace(unsigned quota)
67{
68    return adoptRef(new StorageNamespaceImpl(SessionStorage, String(), quota));
69}
70
71StorageNamespaceImpl::StorageNamespaceImpl(StorageType storageType, const String& path, unsigned quota)
72    : m_storageType(storageType)
73    , m_path(path.crossThreadString())
74    , m_syncManager(0)
75    , m_quota(quota)
76    , m_isShutdown(false)
77{
78    if (m_storageType == LocalStorage && !m_path.isEmpty())
79        m_syncManager = StorageSyncManager::create(m_path);
80}
81
82StorageNamespaceImpl::~StorageNamespaceImpl()
83{
84    ASSERT(isMainThread());
85
86    if (m_storageType == LocalStorage) {
87        ASSERT(localStorageNamespaceMap().get(m_path) == this);
88        localStorageNamespaceMap().remove(m_path);
89    }
90
91    if (!m_isShutdown)
92        close();
93}
94
95PassRefPtr<StorageNamespace> StorageNamespaceImpl::copy()
96{
97    ASSERT(isMainThread());
98    ASSERT(!m_isShutdown);
99    ASSERT(m_storageType == SessionStorage);
100
101    RefPtr<StorageNamespaceImpl> newNamespace = adoptRef(new StorageNamespaceImpl(m_storageType, m_path, m_quota));
102
103    StorageAreaMap::iterator end = m_storageAreaMap.end();
104    for (StorageAreaMap::iterator i = m_storageAreaMap.begin(); i != end; ++i)
105        newNamespace->m_storageAreaMap.set(i->first, i->second->copy());
106    return newNamespace.release();
107}
108
109PassRefPtr<StorageArea> StorageNamespaceImpl::storageArea(PassRefPtr<SecurityOrigin> prpOrigin)
110{
111    ASSERT(isMainThread());
112    ASSERT(!m_isShutdown);
113
114    RefPtr<SecurityOrigin> origin = prpOrigin;
115    RefPtr<StorageAreaImpl> storageArea;
116    if ((storageArea = m_storageAreaMap.get(origin)))
117        return storageArea.release();
118
119    storageArea = StorageAreaImpl::create(m_storageType, origin, m_syncManager, m_quota);
120    m_storageAreaMap.set(origin.release(), storageArea);
121    return storageArea.release();
122}
123
124void StorageNamespaceImpl::close()
125{
126    ASSERT(isMainThread());
127
128    if (m_isShutdown)
129        return;
130
131    // If we're session storage, we shouldn't need to do any work here.
132    if (m_storageType == SessionStorage) {
133        ASSERT(!m_syncManager);
134        return;
135    }
136
137    StorageAreaMap::iterator end = m_storageAreaMap.end();
138    for (StorageAreaMap::iterator it = m_storageAreaMap.begin(); it != end; ++it)
139        it->second->close();
140
141    if (m_syncManager)
142        m_syncManager->close();
143
144    m_isShutdown = true;
145}
146
147#ifdef ANDROID
148void StorageNamespaceImpl::clear(Page* page)
149{
150    ASSERT(isMainThread());
151    if (m_isShutdown)
152        return;
153
154    // Clear all the keys for each of the storage areas.
155    StorageAreaMap::iterator end = m_storageAreaMap.end();
156    for (StorageAreaMap::iterator it = m_storageAreaMap.begin(); it != end; ++it) {
157        // if there is no page provided, then the user tried to clear storage
158        // with only pages in private browsing mode open. So we do not need to
159        // provide a Frame* here (as the frame is only used to dispatch storage events
160        // and private browsing pages won't be using them).
161        it->second->clear(page ? page->mainFrame() : 0);
162    }
163}
164#endif
165
166void StorageNamespaceImpl::unlock()
167{
168    // Because there's a single event loop per-process, this is a no-op.
169}
170
171void StorageNamespaceImpl::clearOriginForDeletion(SecurityOrigin* origin)
172{
173    ASSERT(isMainThread());
174
175    RefPtr<StorageAreaImpl> storageArea = m_storageAreaMap.get(origin);
176    if (storageArea)
177        storageArea->clearForOriginDeletion();
178}
179
180void StorageNamespaceImpl::clearAllOriginsForDeletion()
181{
182    ASSERT(isMainThread());
183
184    StorageAreaMap::iterator end = m_storageAreaMap.end();
185    for (StorageAreaMap::iterator it = m_storageAreaMap.begin(); it != end; ++it)
186        it->second->clearForOriginDeletion();
187}
188
189void StorageNamespaceImpl::sync()
190{
191    ASSERT(isMainThread());
192    StorageAreaMap::iterator end = m_storageAreaMap.end();
193    for (StorageAreaMap::iterator it = m_storageAreaMap.begin(); it != end; ++it)
194        it->second->sync();
195}
196
197} // namespace WebCore
198
199#endif // ENABLE(DOM_STORAGE)
200