initial import
[vuplus_webkit] / Source / WebCore / storage / IDBDatabaseBackendImpl.cpp
1 /*
2  * Copyright (C) 2011 Google 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  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "IDBDatabaseBackendImpl.h"
28
29 #if ENABLE(INDEXED_DATABASE)
30
31 #include "CrossThreadTask.h"
32 #include "DOMStringList.h"
33 #include "IDBBackingStore.h"
34 #include "IDBDatabaseException.h"
35 #include "IDBFactoryBackendImpl.h"
36 #include "IDBObjectStoreBackendImpl.h"
37 #include "IDBTransactionBackendImpl.h"
38 #include "IDBTransactionCoordinator.h"
39
40 namespace WebCore {
41
42 class IDBDatabaseBackendImpl::PendingSetVersionCall : public RefCounted<PendingSetVersionCall> {
43 public:
44     static PassRefPtr<PendingSetVersionCall> create(const String& version, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks)
45     {
46         return adoptRef(new PendingSetVersionCall(version, callbacks, databaseCallbacks));
47     }
48     String version() { return m_version; }
49     PassRefPtr<IDBCallbacks> callbacks() { return m_callbacks; }
50     PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks() { return m_databaseCallbacks; }
51
52 private:
53     PendingSetVersionCall(const String& version, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks)
54         : m_version(version)
55         , m_callbacks(callbacks)
56         , m_databaseCallbacks(databaseCallbacks)
57     {
58     }
59     String m_version;
60     RefPtr<IDBCallbacks> m_callbacks;
61     RefPtr<IDBDatabaseCallbacks> m_databaseCallbacks;
62 };
63
64 IDBDatabaseBackendImpl::IDBDatabaseBackendImpl(const String& name, IDBBackingStore* backingStore, IDBTransactionCoordinator* coordinator, IDBFactoryBackendImpl* factory, const String& uniqueIdentifier)
65     : m_backingStore(backingStore)
66     , m_id(InvalidId)
67     , m_name(name)
68     , m_version("")
69     , m_identifier(uniqueIdentifier)
70     , m_factory(factory)
71     , m_transactionCoordinator(coordinator)
72 {
73     ASSERT(!m_name.isNull());
74
75     bool success = m_backingStore->extractIDBDatabaseMetaData(m_name, m_version, m_id);
76     ASSERT_UNUSED(success, success == (m_id != InvalidId));
77     if (!m_backingStore->setIDBDatabaseMetaData(m_name, m_version, m_id, m_id == InvalidId))
78         ASSERT_NOT_REACHED(); // FIXME: Need better error handling.
79     loadObjectStores();
80 }
81
82 IDBDatabaseBackendImpl::~IDBDatabaseBackendImpl()
83 {
84     m_factory->removeIDBDatabaseBackend(m_identifier);
85 }
86
87 PassRefPtr<IDBBackingStore> IDBDatabaseBackendImpl::backingStore() const
88 {
89     return m_backingStore;
90 }
91
92 PassRefPtr<DOMStringList> IDBDatabaseBackendImpl::objectStoreNames() const
93 {
94     RefPtr<DOMStringList> objectStoreNames = DOMStringList::create();
95     for (ObjectStoreMap::const_iterator it = m_objectStores.begin(); it != m_objectStores.end(); ++it)
96         objectStoreNames->append(it->first);
97     return objectStoreNames.release();
98 }
99
100 PassRefPtr<IDBObjectStoreBackendInterface> IDBDatabaseBackendImpl::createObjectStore(const String& name, const String& keyPath, bool autoIncrement, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec)
101 {
102     ASSERT(transactionPtr->mode() == IDBTransaction::VERSION_CHANGE);
103
104     if (m_objectStores.contains(name)) {
105         ec = IDBDatabaseException::CONSTRAINT_ERR;
106         return 0;
107     }
108
109     RefPtr<IDBObjectStoreBackendImpl> objectStore = IDBObjectStoreBackendImpl::create(m_backingStore.get(), m_id, name, keyPath, autoIncrement);
110     ASSERT(objectStore->name() == name);
111
112     RefPtr<IDBDatabaseBackendImpl> database = this;
113     RefPtr<IDBTransactionBackendInterface> transaction = transactionPtr;
114     if (!transaction->scheduleTask(createCallbackTask(&IDBDatabaseBackendImpl::createObjectStoreInternal, database, objectStore, transaction),
115                                    createCallbackTask(&IDBDatabaseBackendImpl::removeObjectStoreFromMap, database, objectStore))) {
116         ec = IDBDatabaseException::NOT_ALLOWED_ERR;
117         return 0;
118     }
119
120     m_objectStores.set(name, objectStore);
121     return objectStore.release();
122 }
123
124 void IDBDatabaseBackendImpl::createObjectStoreInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore,  PassRefPtr<IDBTransactionBackendInterface> transaction)
125 {
126     int64_t objectStoreId;
127
128     if (!database->m_backingStore->createObjectStore(database->id(), objectStore->name(), objectStore->keyPath(), objectStore->autoIncrement(), objectStoreId)) {
129         transaction->abort();
130         return;
131     }
132
133     objectStore->setId(objectStoreId);
134     transaction->didCompleteTaskEvents();
135 }
136
137 PassRefPtr<IDBObjectStoreBackendInterface> IDBDatabaseBackendImpl::objectStore(const String& name)
138 {
139     return m_objectStores.get(name);
140 }
141
142 void IDBDatabaseBackendImpl::deleteObjectStore(const String& name, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec)
143 {
144     RefPtr<IDBObjectStoreBackendImpl> objectStore = m_objectStores.get(name);
145     if (!objectStore) {
146         ec = IDBDatabaseException::NOT_FOUND_ERR;
147         return;
148     }
149     RefPtr<IDBDatabaseBackendImpl> database = this;
150     RefPtr<IDBTransactionBackendInterface> transaction = transactionPtr;
151     if (!transaction->scheduleTask(createCallbackTask(&IDBDatabaseBackendImpl::deleteObjectStoreInternal, database, objectStore, transaction),
152                                    createCallbackTask(&IDBDatabaseBackendImpl::addObjectStoreToMap, database, objectStore))) {
153         ec = IDBDatabaseException::NOT_ALLOWED_ERR;
154         return;
155     }
156     m_objectStores.remove(name);
157 }
158
159 void IDBDatabaseBackendImpl::deleteObjectStoreInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBTransactionBackendInterface> transaction)
160 {
161     database->m_backingStore->deleteObjectStore(database->id(), objectStore->id());
162     transaction->didCompleteTaskEvents();
163 }
164
165 void IDBDatabaseBackendImpl::setVersion(const String& version, PassRefPtr<IDBCallbacks> prpCallbacks, PassRefPtr<IDBDatabaseCallbacks> prpDatabaseCallbacks, ExceptionCode& ec)
166 {
167     RefPtr<IDBCallbacks> callbacks = prpCallbacks;
168     RefPtr<IDBDatabaseCallbacks> databaseCallbacks = prpDatabaseCallbacks;
169     if (!m_databaseCallbacksSet.contains(databaseCallbacks)) {
170         callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::ABORT_ERR, "Connection was closed before set version transaction was created"));
171         return;
172     }
173     for (DatabaseCallbacksSet::const_iterator it = m_databaseCallbacksSet.begin(); it != m_databaseCallbacksSet.end(); ++it) {
174         if (*it != databaseCallbacks)
175             (*it)->onVersionChange(version);
176     }
177     if (m_databaseCallbacksSet.size() > 1) {
178         callbacks->onBlocked();
179         RefPtr<PendingSetVersionCall> pendingSetVersionCall = PendingSetVersionCall::create(version, callbacks, databaseCallbacks);
180         m_pendingSetVersionCalls.append(pendingSetVersionCall);
181         return;
182     }
183
184     RefPtr<DOMStringList> objectStoreNames = DOMStringList::create();
185     RefPtr<IDBDatabaseBackendImpl> database = this;
186     RefPtr<IDBTransactionBackendInterface> transaction = IDBTransactionBackendImpl::create(objectStoreNames.get(), IDBTransaction::VERSION_CHANGE, this);
187     if (!transaction->scheduleTask(createCallbackTask(&IDBDatabaseBackendImpl::setVersionInternal, database, version, callbacks, transaction),
188                                    createCallbackTask(&IDBDatabaseBackendImpl::resetVersion, database, m_version))) {
189         ec = IDBDatabaseException::NOT_ALLOWED_ERR;
190     }
191 }
192
193 void IDBDatabaseBackendImpl::setVersionInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, const String& version, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendInterface> transaction)
194 {
195     int64_t databaseId = database->id();
196     database->m_version = version;
197     if (!database->m_backingStore->setIDBDatabaseMetaData(database->m_name, database->m_version, databaseId, databaseId == InvalidId)) {
198         // FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors.
199         callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage."));
200         transaction->abort();
201         return;
202     }
203     callbacks->onSuccess(transaction);
204 }
205
206 PassRefPtr<IDBTransactionBackendInterface> IDBDatabaseBackendImpl::transaction(DOMStringList* objectStoreNames, unsigned short mode, ExceptionCode& ec)
207 {
208     for (size_t i = 0; i < objectStoreNames->length(); ++i) {
209         if (!m_objectStores.contains(objectStoreNames->item(i))) {
210             ec = IDBDatabaseException::NOT_FOUND_ERR;
211             return 0;
212         }
213     }
214
215     // FIXME: Return not allowed err if close has been called.
216     return IDBTransactionBackendImpl::create(objectStoreNames, mode, this);
217 }
218
219 void IDBDatabaseBackendImpl::open(PassRefPtr<IDBDatabaseCallbacks> callbacks)
220 {
221     m_databaseCallbacksSet.add(RefPtr<IDBDatabaseCallbacks>(callbacks));
222 }
223
224 void IDBDatabaseBackendImpl::close(PassRefPtr<IDBDatabaseCallbacks> prpCallbacks)
225 {
226     RefPtr<IDBDatabaseCallbacks> callbacks = prpCallbacks;
227     ASSERT(m_databaseCallbacksSet.contains(callbacks));
228     m_databaseCallbacksSet.remove(callbacks);
229     if (m_databaseCallbacksSet.size() > 1)
230         return;
231
232     while (!m_pendingSetVersionCalls.isEmpty()) {
233         ExceptionCode ec = 0;
234         RefPtr<PendingSetVersionCall> pendingSetVersionCall = m_pendingSetVersionCalls.takeFirst();
235         setVersion(pendingSetVersionCall->version(), pendingSetVersionCall->callbacks(), pendingSetVersionCall->databaseCallbacks(), ec);
236         ASSERT(!ec);
237     }
238 }
239
240 void IDBDatabaseBackendImpl::loadObjectStores()
241 {
242     Vector<int64_t> ids;
243     Vector<String> names;
244     Vector<String> keyPaths;
245     Vector<bool> autoIncrementFlags;
246     m_backingStore->getObjectStores(m_id, ids, names, keyPaths, autoIncrementFlags);
247
248     ASSERT(names.size() == ids.size());
249     ASSERT(keyPaths.size() == ids.size());
250     ASSERT(autoIncrementFlags.size() == ids.size());
251
252     for (size_t i = 0; i < ids.size(); i++)
253         m_objectStores.set(names[i], IDBObjectStoreBackendImpl::create(m_backingStore.get(), m_id, ids[i], names[i], keyPaths[i], autoIncrementFlags[i]));
254 }
255
256 void IDBDatabaseBackendImpl::removeObjectStoreFromMap(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore)
257 {
258     ASSERT(database->m_objectStores.contains(objectStore->name()));
259     database->m_objectStores.remove(objectStore->name());
260 }
261
262 void IDBDatabaseBackendImpl::addObjectStoreToMap(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore)
263 {
264     RefPtr<IDBObjectStoreBackendImpl> objectStorePtr = objectStore;
265     ASSERT(!database->m_objectStores.contains(objectStorePtr->name()));
266     database->m_objectStores.set(objectStorePtr->name(), objectStorePtr);
267 }
268
269 void IDBDatabaseBackendImpl::resetVersion(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, const String& version)
270 {
271     database->m_version = version;
272 }
273
274
275 } // namespace WebCore
276
277 #endif // ENABLE(INDEXED_DATABASE)