68bc17165df725e7141c81c188fc756a41c52926
[WebKit-https.git] / Source / WebCore / Modules / indexeddb / IDBObjectStore.cpp
1 /*
2  * Copyright (C) 2010 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 "IDBObjectStore.h"
28
29 #if ENABLE(INDEXED_DATABASE)
30
31 #include "DOMStringList.h"
32 #include "IDBAny.h"
33 #include "IDBDatabase.h"
34 #include "IDBDatabaseException.h"
35 #include "IDBIndex.h"
36 #include "IDBKey.h"
37 #include "IDBKeyPath.h"
38 #include "IDBKeyRange.h"
39 #include "IDBTracing.h"
40 #include "IDBTransaction.h"
41 #include "SerializedScriptValue.h"
42 #include <wtf/UnusedParam.h>
43
44 namespace WebCore {
45
46 static const unsigned short defaultDirection = IDBCursor::NEXT;
47
48 IDBObjectStore::IDBObjectStore(const IDBObjectStoreMetadata& metadata, PassRefPtr<IDBObjectStoreBackendInterface> idbObjectStore, IDBTransaction* transaction)
49     : m_metadata(metadata)
50     , m_backend(idbObjectStore)
51     , m_transaction(transaction)
52     , m_deleted(false)
53 {
54     ASSERT(m_backend);
55     ASSERT(m_transaction);
56     // We pass a reference to this object before it can be adopted.
57     relaxAdoptionRequirement();
58 }
59
60 PassRefPtr<DOMStringList> IDBObjectStore::indexNames() const
61 {
62     IDB_TRACE("IDBObjectStore::indexNames");
63     RefPtr<DOMStringList> indexNames = DOMStringList::create();
64     for (IDBObjectStoreMetadata::IndexMap::const_iterator it = m_metadata.indexes.begin(); it != m_metadata.indexes.end(); ++it)
65         indexNames->append(it->first);
66     indexNames->sort();
67     return indexNames.release();
68 }
69
70 PassRefPtr<IDBRequest> IDBObjectStore::get(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> keyRange, ExceptionCode& ec)
71 {
72     IDB_TRACE("IDBObjectStore::get");
73     if (m_deleted) {
74         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
75         return 0;
76     }
77     if (!keyRange) {
78         ec = IDBDatabaseException::DATA_ERR;
79         return 0;
80     }
81     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
82     m_backend->get(keyRange, request, m_transaction->backend(), ec);
83     if (ec) {
84         request->markEarlyDeath();
85         return 0;
86     }
87     return request.release();
88 }
89
90 PassRefPtr<IDBRequest> IDBObjectStore::get(ScriptExecutionContext* context, PassRefPtr<IDBKey> key, ExceptionCode& ec)
91 {
92     IDB_TRACE("IDBObjectStore::get");
93     RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(key, ec);
94     if (ec)
95         return 0;
96     return get(context, keyRange.release(), ec);
97 }
98
99 PassRefPtr<IDBRequest> IDBObjectStore::add(ScriptExecutionContext* context, PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBKey> key, ExceptionCode& ec)
100 {
101     IDB_TRACE("IDBObjectStore::add");
102     if (m_deleted) {
103         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
104         return 0;
105     }
106     if (m_transaction->isReadOnly()) {
107         ec = IDBDatabaseException::READ_ONLY_ERR;
108         return 0;
109     }
110
111     if (key && !key->isValid()) {
112         ec = IDBDatabaseException::DATA_ERR;
113         return 0;
114     }
115
116     RefPtr<SerializedScriptValue> value = prpValue;
117     if (value->blobURLs().size() > 0) {
118         // FIXME: Add Blob/File/FileList support
119         ec = IDBDatabaseException::IDB_DATA_CLONE_ERR;
120         return 0;
121     }
122
123     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
124     m_backend->put(value, key, IDBObjectStoreBackendInterface::AddOnly, request, m_transaction->backend(), ec);
125     if (ec) {
126         request->markEarlyDeath();
127         return 0;
128     }
129     return request.release();
130 }
131
132 PassRefPtr<IDBRequest> IDBObjectStore::put(ScriptExecutionContext* context, PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBKey> key, ExceptionCode& ec)
133 {
134     IDB_TRACE("IDBObjectStore::put");
135     if (m_deleted) {
136         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
137         return 0;
138     }
139     if (m_transaction->isReadOnly()) {
140         ec = IDBDatabaseException::READ_ONLY_ERR;
141         return 0;
142     }
143
144     if (key && !key->isValid()) {
145         ec = IDBDatabaseException::DATA_ERR;
146         return 0;
147     }
148
149     RefPtr<SerializedScriptValue> value = prpValue;
150     if (value->blobURLs().size() > 0) {
151         // FIXME: Add Blob/File/FileList support
152         ec = IDBDatabaseException::IDB_DATA_CLONE_ERR;
153         return 0;
154     }
155
156     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
157     m_backend->put(value, key, IDBObjectStoreBackendInterface::AddOrUpdate, request, m_transaction->backend(), ec);
158     if (ec) {
159         request->markEarlyDeath();
160         return 0;
161     }
162     return request.release();
163 }
164
165 PassRefPtr<IDBRequest> IDBObjectStore::deleteFunction(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> keyRange, ExceptionCode& ec)
166 {
167     IDB_TRACE("IDBObjectStore::delete");
168     if (m_deleted) {
169         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
170         return 0;
171     }
172     if (m_transaction->isReadOnly()) {
173         ec = IDBDatabaseException::READ_ONLY_ERR;
174         return 0;
175     }
176
177     if (!keyRange) {
178         ec = IDBDatabaseException::DATA_ERR;
179         return 0;
180     }
181
182     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
183     m_backend->deleteFunction(keyRange, request, m_transaction->backend(), ec);
184     if (ec) {
185         request->markEarlyDeath();
186         return 0;
187     }
188     return request.release();
189 }
190
191 PassRefPtr<IDBRequest> IDBObjectStore::deleteFunction(ScriptExecutionContext* context, PassRefPtr<IDBKey> key, ExceptionCode& ec)
192 {
193     IDB_TRACE("IDBObjectStore::delete");
194     RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(key, ec);
195     if (ec)
196         return 0;
197     return deleteFunction(context, keyRange.release(), ec);
198 }
199
200 PassRefPtr<IDBRequest> IDBObjectStore::clear(ScriptExecutionContext* context, ExceptionCode& ec)
201 {
202     IDB_TRACE("IDBObjectStore::clear");
203     if (m_deleted) {
204         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
205         return 0;
206     }
207     if (m_transaction->isReadOnly()) {
208         ec = IDBDatabaseException::READ_ONLY_ERR;
209         return 0;
210     }
211
212     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
213     m_backend->clear(request, m_transaction->backend(), ec);
214     if (ec) {
215         request->markEarlyDeath();
216         return 0;
217     }
218     return request.release();
219 }
220
221 PassRefPtr<IDBIndex> IDBObjectStore::createIndex(const String& name, const String& keyPath, const Dictionary& options, ExceptionCode& ec)
222 {
223     return createIndex(name, IDBKeyPath(keyPath), options, ec);
224 }
225
226 PassRefPtr<IDBIndex> IDBObjectStore::createIndex(const String& name, PassRefPtr<DOMStringList> keyPath, const Dictionary& options, ExceptionCode& ec)
227 {
228     // FIXME: Binding code for DOMString[] should not match null. http://webkit.org/b/84217
229     if (!keyPath)
230         return createIndex(name, IDBKeyPath("null"), options, ec);
231     return createIndex(name, IDBKeyPath(*keyPath), options, ec);
232 }
233
234
235 PassRefPtr<IDBIndex> IDBObjectStore::createIndex(const String& name, const IDBKeyPath& keyPath, const Dictionary& options, ExceptionCode& ec)
236 {
237     IDB_TRACE("IDBObjectStore::createIndex");
238     if (!m_transaction->isVersionChange() || m_deleted) {
239         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
240         return 0;
241     }
242
243     if (!keyPath.isValid()) {
244         ec = IDBDatabaseException::IDB_SYNTAX_ERR;
245         return 0;
246     }
247
248     bool unique = false;
249     options.get("unique", unique);
250
251     bool multiEntry = false;
252     options.get("multiEntry", multiEntry);
253
254     if (keyPath.type() == IDBKeyPath::ArrayType && multiEntry) {
255         ec = IDBDatabaseException::IDB_NOT_SUPPORTED_ERR;
256         return 0;
257     }
258
259     RefPtr<IDBIndexBackendInterface> indexBackend = m_backend->createIndex(name, keyPath, unique, multiEntry, m_transaction->backend(), ec);
260     ASSERT(!indexBackend != !ec); // If we didn't get an index, we should have gotten an exception code. And vice versa.
261     if (ec)
262         return 0;
263
264     IDBIndexMetadata metadata(name, keyPath, unique, multiEntry);
265     RefPtr<IDBIndex> index = IDBIndex::create(metadata, indexBackend.release(), this, m_transaction.get());
266     m_indexMap.set(name, index);
267     m_metadata.indexes.set(name, metadata);
268
269     return index.release();
270 }
271
272 PassRefPtr<IDBIndex> IDBObjectStore::index(const String& name, ExceptionCode& ec)
273 {
274     IDB_TRACE("IDBObjectStore::index");
275     if (m_deleted) {
276         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
277         return 0;
278     }
279     if (m_transaction->isFinished()) {
280         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
281         return 0;
282     }
283
284     IDBIndexMap::iterator it = m_indexMap.find(name);
285     if (it != m_indexMap.end())
286         return it->second;
287
288     RefPtr<IDBIndexBackendInterface> indexBackend = m_backend->index(name, ec);
289     ASSERT(!indexBackend != !ec); // If we didn't get an index, we should have gotten an exception code. And vice versa.
290     if (ec)
291         return 0;
292
293     IDBObjectStoreMetadata::IndexMap::const_iterator mdit = m_metadata.indexes.find(name);
294     ASSERT(mdit != m_metadata.indexes.end());
295
296     RefPtr<IDBIndex> index = IDBIndex::create(mdit->second, indexBackend.release(), this, m_transaction.get());
297     m_indexMap.set(name, index);
298     return index.release();
299 }
300
301 void IDBObjectStore::deleteIndex(const String& name, ExceptionCode& ec)
302 {
303     if (!m_transaction->isVersionChange() || m_deleted) {
304         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
305         return;
306     }
307
308     m_backend->deleteIndex(name, m_transaction->backend(), ec);
309     if (!ec) {
310         IDBIndexMap::iterator it = m_indexMap.find(name);
311         if (it != m_indexMap.end()) {
312             it->second->markDeleted();
313             m_indexMap.remove(name);
314         }
315
316         ASSERT(m_metadata.indexes.contains(name));
317         m_metadata.indexes.remove(name);
318     }
319 }
320
321 PassRefPtr<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> range, const String& directionString, ExceptionCode& ec)
322 {
323     IDB_TRACE("IDBObjectStore::openCursor");
324     if (m_deleted) {
325         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
326         return 0;
327     }
328     unsigned short direction = IDBCursor::stringToDirection(directionString, ec);
329     if (ec)
330         return 0;
331
332     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
333     request->setCursorType(IDBCursorBackendInterface::ObjectStoreCursor);
334     m_backend->openCursor(range, direction, request, m_transaction->backend(), ec);
335     if (ec) {
336         request->markEarlyDeath();
337         return 0;
338     }
339     return request.release();
340 }
341
342 PassRefPtr<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> range, unsigned short direction, ExceptionCode& ec)
343 {
344     IDB_TRACE("IDBObjectStore::openCursor");
345     DEFINE_STATIC_LOCAL(String, consoleMessage, ("Numeric direction values are deprecated in IDBObjectStore.openCursor. Use\"next\", \"nextunique\", \"prev\", or \"prevunique\"."));
346     context->addConsoleMessage(JSMessageSource, LogMessageType, WarningMessageLevel, consoleMessage);
347     const String& directionString = IDBCursor::directionToString(direction, ec);
348     if (ec)
349         return 0;
350     return openCursor(context, range, directionString, ec);
351 }
352
353 PassRefPtr<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, PassRefPtr<IDBKey> key, const String& direction, ExceptionCode& ec)
354 {
355     IDB_TRACE("IDBObjectStore::openCursor");
356     RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(key, ec);
357     if (ec)
358         return 0;
359     return openCursor(context, keyRange.release(), ec);
360 }
361
362 PassRefPtr<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, PassRefPtr<IDBKey> key, unsigned short direction, ExceptionCode& ec)
363 {
364     IDB_TRACE("IDBObjectStore::openCursor");
365     RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(key, ec);
366     if (ec)
367         return 0;
368     return openCursor(context, keyRange.release(), direction, ec);
369 }
370
371 PassRefPtr<IDBRequest> IDBObjectStore::count(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> range, ExceptionCode& ec)
372 {
373     IDB_TRACE("IDBObjectStore::count");
374     if (m_deleted) {
375         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
376         return 0;
377     }
378     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
379     m_backend->count(range, request, m_transaction->backend(), ec);
380     if (ec) {
381         request->markEarlyDeath();
382         return 0;
383     }
384     return request.release();
385 }
386
387 PassRefPtr<IDBRequest> IDBObjectStore::count(ScriptExecutionContext* context, PassRefPtr<IDBKey> key, ExceptionCode& ec)
388 {
389     IDB_TRACE("IDBObjectStore::count");
390     RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(key, ec);
391     if (ec)
392         return 0;
393     return count(context, keyRange.release(), ec);
394 }
395
396 void IDBObjectStore::transactionFinished()
397 {
398     ASSERT(m_transaction->isFinished());
399
400     // Break reference cycles.
401     m_indexMap.clear();
402 }
403
404
405 } // namespace WebCore
406
407 #endif // ENABLE(INDEXED_DATABASE)