891253d9e04c91e95aeeb2304545f2466a02725e
[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 "IDBBindingUtilities.h"
34 #include "IDBCursorBackendInterface.h"
35 #include "IDBCursorWithValue.h"
36 #include "IDBDatabase.h"
37 #include "IDBDatabaseException.h"
38 #include "IDBIndex.h"
39 #include "IDBKey.h"
40 #include "IDBKeyPath.h"
41 #include "IDBKeyRange.h"
42 #include "IDBTracing.h"
43 #include "IDBTransaction.h"
44 #include "ScriptExecutionContext.h"
45 #include "SerializedScriptValue.h"
46 #include <wtf/UnusedParam.h>
47
48 namespace WebCore {
49
50 static const unsigned short defaultDirection = IDBCursor::NEXT;
51
52 IDBObjectStore::IDBObjectStore(const IDBObjectStoreMetadata& metadata, PassRefPtr<IDBObjectStoreBackendInterface> idbObjectStore, IDBTransaction* transaction)
53     : m_metadata(metadata)
54     , m_backend(idbObjectStore)
55     , m_transaction(transaction)
56     , m_deleted(false)
57 {
58     ASSERT(m_backend);
59     ASSERT(m_transaction);
60     // We pass a reference to this object before it can be adopted.
61     relaxAdoptionRequirement();
62 }
63
64 PassRefPtr<DOMStringList> IDBObjectStore::indexNames() const
65 {
66     IDB_TRACE("IDBObjectStore::indexNames");
67     RefPtr<DOMStringList> indexNames = DOMStringList::create();
68     for (IDBObjectStoreMetadata::IndexMap::const_iterator it = m_metadata.indexes.begin(); it != m_metadata.indexes.end(); ++it)
69         indexNames->append(it->value.name);
70     indexNames->sort();
71     return indexNames.release();
72 }
73
74 PassRefPtr<IDBRequest> IDBObjectStore::get(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> keyRange, ExceptionCode& ec)
75 {
76     IDB_TRACE("IDBObjectStore::get");
77     if (m_deleted) {
78         ec = IDBDatabaseException::InvalidStateError;
79         return 0;
80     }
81     if (!keyRange) {
82         ec = IDBDatabaseException::DataError;
83         return 0;
84     }
85     if (!m_transaction->isActive()) {
86         ec = IDBDatabaseException::TransactionInactiveError;
87         return 0;
88     }
89     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
90     backendDB()->get(m_transaction->id(), id(), IDBIndexMetadata::InvalidId, keyRange, false, request);
91     return request.release();
92 }
93
94 PassRefPtr<IDBRequest> IDBObjectStore::get(ScriptExecutionContext* context, PassRefPtr<IDBKey> key, ExceptionCode& ec)
95 {
96     RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(key, ec);
97     if (ec)
98         return 0;
99     return get(context, keyRange.release(), ec);
100 }
101
102 static void generateIndexKeysForValue(DOMRequestState* requestState, const IDBIndexMetadata& indexMetadata, const ScriptValue& objectValue, IDBObjectStore::IndexKeys* indexKeys)
103 {
104     ASSERT(indexKeys);
105     RefPtr<IDBKey> indexKey = createIDBKeyFromScriptValueAndKeyPath(requestState, objectValue, indexMetadata.keyPath);
106
107     if (!indexKey)
108         return;
109
110     if (!indexMetadata.multiEntry || indexKey->type() != IDBKey::ArrayType) {
111         if (!indexKey->isValid())
112             return;
113
114         indexKeys->append(indexKey);
115     } else {
116         ASSERT(indexMetadata.multiEntry);
117         ASSERT(indexKey->type() == IDBKey::ArrayType);
118         indexKey = IDBKey::createMultiEntryArray(indexKey->array());
119
120         for (size_t i = 0; i < indexKey->array().size(); ++i)
121             indexKeys->append(indexKey->array()[i]);
122     }
123 }
124
125 PassRefPtr<IDBRequest> IDBObjectStore::add(ScriptState* state, ScriptValue& value, PassRefPtr<IDBKey> key, ExceptionCode& ec)
126 {
127     IDB_TRACE("IDBObjectStore::add");
128     return put(IDBObjectStoreBackendInterface::AddOnly, IDBAny::create(this), state, value, key, ec);
129 }
130
131 PassRefPtr<IDBRequest> IDBObjectStore::put(ScriptState* state, ScriptValue& value, PassRefPtr<IDBKey> key, ExceptionCode& ec)
132 {
133     IDB_TRACE("IDBObjectStore::put");
134     return put(IDBObjectStoreBackendInterface::AddOrUpdate, IDBAny::create(this), state, value, key, ec);
135 }
136
137 PassRefPtr<IDBRequest> IDBObjectStore::put(IDBObjectStoreBackendInterface::PutMode putMode, PassRefPtr<IDBAny> source, ScriptState* state, ScriptValue& value, PassRefPtr<IDBKey> prpKey, ExceptionCode& ec)
138 {
139     IDB_TRACE("IDBObjectStore::put");
140     RefPtr<IDBKey> key = prpKey;
141     if (m_deleted) {
142         ec = IDBDatabaseException::InvalidStateError;
143         return 0;
144     }
145     if (!m_transaction->isActive()) {
146         ec = IDBDatabaseException::TransactionInactiveError;
147         return 0;
148     }
149     if (m_transaction->isReadOnly()) {
150         ec = IDBDatabaseException::ReadOnlyError;
151         return 0;
152     }
153
154     RefPtr<SerializedScriptValue> serializedValue = value.serialize(state);
155     if (state->hadException()) {
156         ec = IDBDatabaseException::DataCloneError;
157         return 0;
158     }
159
160     if (serializedValue->blobURLs().size() > 0) {
161         // FIXME: Add Blob/File/FileList support
162         ec = IDBDatabaseException::DataCloneError;
163         return 0;
164     }
165
166     const IDBKeyPath& keyPath = m_metadata.keyPath;
167     const bool usesInLineKeys = !keyPath.isNull();
168     const bool hasKeyGenerator = autoIncrement();
169
170     ScriptExecutionContext* context = scriptExecutionContextFromScriptState(state);
171     DOMRequestState requestState(context);
172
173     if (putMode != IDBObjectStoreBackendInterface::CursorUpdate && usesInLineKeys && key) {
174         ec = IDBDatabaseException::DataError;
175         return 0;
176     }
177     if (!usesInLineKeys && !hasKeyGenerator && !key) {
178         ec = IDBDatabaseException::DataError;
179         return 0;
180     }
181     if (usesInLineKeys) {
182         RefPtr<IDBKey> keyPathKey = createIDBKeyFromScriptValueAndKeyPath(&requestState, value, keyPath);
183         if (keyPathKey && !keyPathKey->isValid()) {
184             ec = IDBDatabaseException::DataError;
185             return 0;
186         }
187         if (!hasKeyGenerator && !keyPathKey) {
188             ec = IDBDatabaseException::DataError;
189             return 0;
190         }
191         if (hasKeyGenerator && !keyPathKey) {
192             if (!canInjectIDBKeyIntoScriptValue(&requestState, value, keyPath)) {
193                 ec = IDBDatabaseException::DataError;
194                 return 0;
195             }
196         }
197         if (keyPathKey)
198             key = keyPathKey;
199     }
200     if (key && !key->isValid()) {
201         ec = IDBDatabaseException::DataError;
202         return 0;
203     }
204
205     Vector<int64_t> indexIds;
206     Vector<IndexKeys> indexKeys;
207     for (IDBObjectStoreMetadata::IndexMap::const_iterator it = m_metadata.indexes.begin(); it != m_metadata.indexes.end(); ++it) {
208         IndexKeys keys;
209         generateIndexKeysForValue(&requestState, it->value, value, &keys);
210         indexIds.append(it->key);
211         indexKeys.append(keys);
212     }
213
214     RefPtr<IDBRequest> request = IDBRequest::create(context, source, m_transaction.get());
215     Vector<uint8_t> valueBytes = serializedValue->toWireBytes();
216     backendDB()->put(m_transaction->id(), id(), &valueBytes, key.release(), static_cast<IDBDatabaseBackendInterface::PutMode>(putMode), request, indexIds, indexKeys);
217     return request.release();
218 }
219
220 PassRefPtr<IDBRequest> IDBObjectStore::deleteFunction(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> keyRange, ExceptionCode& ec)
221 {
222     IDB_TRACE("IDBObjectStore::delete");
223     if (m_deleted) {
224         ec = IDBDatabaseException::InvalidStateError;
225         return 0;
226     }
227     if (!m_transaction->isActive()) {
228         ec = IDBDatabaseException::TransactionInactiveError;
229         return 0;
230     }
231     if (m_transaction->isReadOnly()) {
232         ec = IDBDatabaseException::ReadOnlyError;
233         return 0;
234     }
235     if (!keyRange) {
236         ec = IDBDatabaseException::DataError;
237         return 0;
238     }
239
240     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
241     backendDB()->deleteRange(m_transaction->id(), id(), keyRange, request);
242     return request.release();
243 }
244
245 PassRefPtr<IDBRequest> IDBObjectStore::deleteFunction(ScriptExecutionContext* context, PassRefPtr<IDBKey> key, ExceptionCode& ec)
246 {
247     RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(key, ec);
248     if (ec)
249         return 0;
250     return deleteFunction(context, keyRange.release(), ec);
251 }
252
253 PassRefPtr<IDBRequest> IDBObjectStore::clear(ScriptExecutionContext* context, ExceptionCode& ec)
254 {
255     IDB_TRACE("IDBObjectStore::clear");
256     if (m_deleted) {
257         ec = IDBDatabaseException::InvalidStateError;
258         return 0;
259     }
260     if (!m_transaction->isActive()) {
261         ec = IDBDatabaseException::TransactionInactiveError;
262         return 0;
263     }
264     if (m_transaction->isReadOnly()) {
265         ec = IDBDatabaseException::ReadOnlyError;
266         return 0;
267     }
268
269     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
270     backendDB()->clear(m_transaction->id(), id(), request);
271     return request.release();
272 }
273
274 namespace {
275 // This class creates the index keys for a given index by extracting
276 // them from the SerializedScriptValue, for all the existing values in
277 // the objectStore. It only needs to be kept alive by virtue of being
278 // a listener on an IDBRequest object, in the same way that JavaScript
279 // cursor success handlers are kept alive.
280 class IndexPopulator : public EventListener {
281 public:
282     static PassRefPtr<IndexPopulator> create(PassRefPtr<IDBDatabaseBackendInterface> backend, int64_t transactionId, int64_t objectStoreId, const IDBIndexMetadata& indexMetadata)
283     {
284         return adoptRef(new IndexPopulator(backend, transactionId, objectStoreId, indexMetadata));
285     }
286
287     virtual bool operator==(const EventListener& other)
288     {
289         return this == &other;
290     }
291
292 private:
293     IndexPopulator(PassRefPtr<IDBDatabaseBackendInterface> backend, int64_t transactionId, int64_t objectStoreId, const IDBIndexMetadata& indexMetadata)
294         : EventListener(CPPEventListenerType)
295         , m_databaseBackend(backend)
296         , m_transactionId(transactionId)
297         , m_objectStoreId(objectStoreId)
298         , m_indexMetadata(indexMetadata)
299     {
300     }
301
302     virtual void handleEvent(ScriptExecutionContext* context, Event* event)
303     {
304         ASSERT(event->type() == eventNames().successEvent);
305         EventTarget* target = event->target();
306         IDBRequest* request = static_cast<IDBRequest*>(target);
307
308         ExceptionCode ec = 0;
309         RefPtr<IDBAny> cursorAny = request->result(ec);
310         ASSERT(!ec);
311         RefPtr<IDBCursorWithValue> cursor;
312         if (cursorAny->type() == IDBAny::IDBCursorWithValueType)
313             cursor = cursorAny->idbCursorWithValue();
314
315         Vector<int64_t, 1> indexIds;
316         indexIds.append(m_indexMetadata.id);
317         if (cursor) {
318             cursor->continueFunction(ec);
319             ASSERT(!ec);
320
321             RefPtr<IDBKey> primaryKey = cursor->idbPrimaryKey();
322             ScriptValue value = cursor->value();
323
324             IDBObjectStore::IndexKeys indexKeys;
325             generateIndexKeysForValue(request->requestState(), m_indexMetadata, value, &indexKeys);
326
327             Vector<IDBObjectStore::IndexKeys, 1> indexKeysList;
328             indexKeysList.append(indexKeys);
329
330             m_databaseBackend->setIndexKeys(m_transactionId, m_objectStoreId, primaryKey, indexIds, indexKeysList);
331         } else {
332             // Now that we are done indexing, tell the backend to go
333             // back to processing tasks of type NormalTask.
334             m_databaseBackend->setIndexesReady(m_transactionId, m_objectStoreId, indexIds);
335             m_databaseBackend.clear();
336         }
337
338     }
339
340     RefPtr<IDBDatabaseBackendInterface> m_databaseBackend;
341     const int64_t m_transactionId;
342     const int64_t m_objectStoreId;
343     const IDBIndexMetadata m_indexMetadata;
344 };
345 }
346
347 PassRefPtr<IDBIndex> IDBObjectStore::createIndex(ScriptExecutionContext* context, const String& name, const IDBKeyPath& keyPath, const Dictionary& options, ExceptionCode& ec)
348 {
349     bool unique = false;
350     options.get("unique", unique);
351
352     bool multiEntry = false;
353     options.get("multiEntry", multiEntry);
354
355     return createIndex(context, name, keyPath, unique, multiEntry, ec);
356 }
357
358 PassRefPtr<IDBIndex> IDBObjectStore::createIndex(ScriptExecutionContext* context, const String& name, const IDBKeyPath& keyPath, bool unique, bool multiEntry, ExceptionCode& ec)
359 {
360     IDB_TRACE("IDBObjectStore::createIndex");
361     if (!m_transaction->isVersionChange() || m_deleted) {
362         ec = IDBDatabaseException::InvalidStateError;
363         return 0;
364     }
365     if (!m_transaction->isActive()) {
366         ec = IDBDatabaseException::TransactionInactiveError;
367         return 0;
368     }
369     if (!keyPath.isValid()) {
370         ec = IDBDatabaseException::SyntaxError;
371         return 0;
372     }
373     if (name.isNull()) {
374         ec = TypeError;
375         return 0;
376     }
377     if (containsIndex(name)) {
378         ec = IDBDatabaseException::ConstraintError;
379         return 0;
380     }
381
382     if (keyPath.type() == IDBKeyPath::ArrayType && multiEntry) {
383         ec = IDBDatabaseException::InvalidAccessError;
384         return 0;
385     }
386
387     int64_t indexId = m_metadata.maxIndexId + 1;
388     m_backend->createIndex(indexId, name, keyPath, unique, multiEntry, m_transaction->backend(), ec);
389     if (ec)
390         return 0;
391
392     ++m_metadata.maxIndexId;
393
394     IDBIndexMetadata metadata(name, indexId, keyPath, unique, multiEntry);
395     RefPtr<IDBIndex> index = IDBIndex::create(metadata, this, m_transaction.get());
396     m_indexMap.set(name, index);
397     m_metadata.indexes.set(indexId, metadata);
398
399     ASSERT(!ec);
400     if (ec)
401         return 0;
402
403     RefPtr<IDBRequest> indexRequest = openCursor(context, static_cast<IDBKeyRange*>(0), IDBCursor::directionNext(), IDBTransactionBackendInterface::PreemptiveTask, ec);
404     ASSERT(!ec);
405     if (ec)
406         return 0;
407     indexRequest->preventPropagation();
408
409     // This is kept alive by being the success handler of the request, which is in turn kept alive by the owning transaction.
410     RefPtr<IndexPopulator> indexPopulator = IndexPopulator::create(backendDB(), m_transaction->id(), id(), metadata);
411     indexRequest->setOnsuccess(indexPopulator);
412
413     return index.release();
414 }
415
416 PassRefPtr<IDBIndex> IDBObjectStore::index(const String& name, ExceptionCode& ec)
417 {
418     IDB_TRACE("IDBObjectStore::index");
419     if (m_deleted) {
420         ec = IDBDatabaseException::InvalidStateError;
421         return 0;
422     }
423     if (m_transaction->isFinished()) {
424         ec = IDBDatabaseException::InvalidStateError;
425         return 0;
426     }
427
428     IDBIndexMap::iterator it = m_indexMap.find(name);
429     if (it != m_indexMap.end())
430         return it->value;
431
432     int64_t indexId = findIndexId(name);
433     if (indexId == IDBIndexMetadata::InvalidId) {
434         ec = IDBDatabaseException::NotFoundError;
435         return 0;
436     }
437
438     const IDBIndexMetadata* indexMetadata(0);
439     for (IDBObjectStoreMetadata::IndexMap::const_iterator it = m_metadata.indexes.begin(); it != m_metadata.indexes.end(); ++it) {
440         if (it->value.name == name) {
441             indexMetadata = &it->value;
442             break;
443         }
444     }
445     ASSERT(indexMetadata);
446     ASSERT(indexMetadata->id != IDBIndexMetadata::InvalidId);
447
448     RefPtr<IDBIndex> index = IDBIndex::create(*indexMetadata, this, m_transaction.get());
449     m_indexMap.set(name, index);
450     return index.release();
451 }
452
453 void IDBObjectStore::deleteIndex(const String& name, ExceptionCode& ec)
454 {
455     if (!m_transaction->isVersionChange() || m_deleted) {
456         ec = IDBDatabaseException::InvalidStateError;
457         return;
458     }
459     if (!m_transaction->isActive()) {
460         ec = IDBDatabaseException::TransactionInactiveError;
461         return;
462     }
463     int64_t indexId = findIndexId(name);
464     if (indexId == IDBIndexMetadata::InvalidId) {
465         ec = IDBDatabaseException::NotFoundError;
466         return;
467     }
468
469     m_backend->deleteIndex(indexId, m_transaction->backend(), ec);
470     if (!ec) {
471         IDBIndexMap::iterator it = m_indexMap.find(name);
472         if (it != m_indexMap.end()) {
473             m_metadata.indexes.remove(it->value->id());
474             it->value->markDeleted();
475             m_indexMap.remove(name);
476         }
477     }
478 }
479
480 PassRefPtr<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> range, const String& directionString, IDBTransactionBackendInterface::TaskType taskType, ExceptionCode& ec)
481 {
482     IDB_TRACE("IDBObjectStore::openCursor");
483     if (m_deleted) {
484         ec = IDBDatabaseException::InvalidStateError;
485         return 0;
486     }
487     if (!m_transaction->isActive()) {
488         ec = IDBDatabaseException::TransactionInactiveError;
489         return 0;
490     }
491     IDBCursor::Direction direction = IDBCursor::stringToDirection(directionString, context, ec);
492     if (ec)
493         return 0;
494
495     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
496     request->setCursorDetails(IDBCursorBackendInterface::KeyAndValue, direction);
497
498     backendDB()->openCursor(m_transaction->id(), id(), IDBIndexMetadata::InvalidId, range, direction, false, static_cast<IDBDatabaseBackendInterface::TaskType>(taskType), request);
499     return request.release();
500 }
501
502 PassRefPtr<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, PassRefPtr<IDBKey> key, const String& direction, ExceptionCode& ec)
503 {
504     RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(key, ec);
505     if (ec)
506         return 0;
507     return openCursor(context, keyRange.release(), direction, ec);
508 }
509
510 PassRefPtr<IDBRequest> IDBObjectStore::count(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> range, ExceptionCode& ec)
511 {
512     IDB_TRACE("IDBObjectStore::count");
513     if (m_deleted) {
514         ec = IDBDatabaseException::InvalidStateError;
515         return 0;
516     }
517     if (!m_transaction->isActive()) {
518         ec = IDBDatabaseException::TransactionInactiveError;
519         return 0;
520     }
521     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
522     backendDB()->count(m_transaction->id(), id(), IDBIndexMetadata::InvalidId, range, request);
523     return request.release();
524 }
525
526 PassRefPtr<IDBRequest> IDBObjectStore::count(ScriptExecutionContext* context, PassRefPtr<IDBKey> key, ExceptionCode& ec)
527 {
528     RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(key, ec);
529     if (ec)
530         return 0;
531     return count(context, keyRange.release(), ec);
532 }
533
534 void IDBObjectStore::transactionFinished()
535 {
536     ASSERT(m_transaction->isFinished());
537
538     // Break reference cycles.
539     m_indexMap.clear();
540 }
541
542 int64_t IDBObjectStore::findIndexId(const String& name) const
543 {
544     for (IDBObjectStoreMetadata::IndexMap::const_iterator it = m_metadata.indexes.begin(); it != m_metadata.indexes.end(); ++it) {
545         if (it->value.name == name) {
546             ASSERT(it->key != IDBIndexMetadata::InvalidId);
547             return it->key;
548         }
549     }
550     return IDBIndexMetadata::InvalidId;
551 }
552
553 IDBDatabaseBackendInterface* IDBObjectStore::backendDB() const
554 {
555     return m_transaction->backendDB();
556 }
557
558 } // namespace WebCore
559
560 #endif // ENABLE(INDEXED_DATABASE)