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