Modern IDB: IDBObjectStore.deleteIndex() support.
[WebKit-https.git] / Source / WebCore / Modules / indexeddb / client / IDBObjectStoreImpl.cpp
1 /*
2  * Copyright (C) 2015 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. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "IDBObjectStoreImpl.h"
28
29 #if ENABLE(INDEXED_DATABASE)
30
31 #include "DOMRequestState.h"
32 #include "IDBBindingUtilities.h"
33 #include "IDBCursorImpl.h"
34 #include "IDBDatabaseException.h"
35 #include "IDBError.h"
36 #include "IDBIndexImpl.h"
37 #include "IDBKey.h"
38 #include "IDBKeyRangeData.h"
39 #include "IDBRequestImpl.h"
40 #include "IDBTransactionImpl.h"
41 #include "IndexedDB.h"
42 #include "Logging.h"
43 #include "SerializedScriptValue.h"
44
45 namespace WebCore {
46 namespace IDBClient {
47
48 Ref<IDBObjectStore> IDBObjectStore::create(const IDBObjectStoreInfo& info, IDBTransaction& transaction)
49 {
50     return adoptRef(*new IDBObjectStore(info, transaction));
51 }
52
53 IDBObjectStore::IDBObjectStore(const IDBObjectStoreInfo& info, IDBTransaction& transaction)
54     : m_info(info)
55     , m_transaction(transaction)
56 {
57 }
58
59 IDBObjectStore::~IDBObjectStore()
60 {
61 }
62
63 const String IDBObjectStore::name() const
64 {
65     return m_info.name();
66 }
67
68 RefPtr<WebCore::IDBAny> IDBObjectStore::keyPathAny() const
69 {
70     return IDBAny::create(m_info.keyPath());
71 }
72
73 const IDBKeyPath IDBObjectStore::keyPath() const
74 {
75     return m_info.keyPath();
76 }
77
78 RefPtr<DOMStringList> IDBObjectStore::indexNames() const
79 {
80     RefPtr<DOMStringList> indexNames = DOMStringList::create();
81     for (auto& name : m_info.indexNames())
82         indexNames->append(name);
83     indexNames->sort();
84
85     return indexNames;
86 }
87
88 RefPtr<WebCore::IDBTransaction> IDBObjectStore::transaction()
89 {
90     return &m_transaction.get();
91 }
92
93 bool IDBObjectStore::autoIncrement() const
94 {
95     return m_info.autoIncrement();
96 }
97
98 RefPtr<WebCore::IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, ExceptionCode& ec)
99 {
100     return openCursor(context, static_cast<IDBKeyRange*>(nullptr), ec);
101 }
102
103 RefPtr<WebCore::IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, IDBKeyRange* keyRange, ExceptionCode& ec)
104 {
105     return openCursor(context, keyRange, IDBCursor::directionNext(), ec);
106 }
107
108 RefPtr<WebCore::IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec)
109 {
110     return openCursor(context, key, IDBCursor::directionNext(), ec);
111 }
112
113 RefPtr<WebCore::IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, IDBKeyRange* range, const String& directionString, ExceptionCode& ec)
114 {
115     LOG(IndexedDB, "IDBObjectStore::openCursor");
116
117     if (m_deleted) {
118         ec = IDBDatabaseException::InvalidStateError;
119         return nullptr;
120     }
121
122     if (!m_transaction->isActive()) {
123         ec = IDBDatabaseException::TransactionInactiveError;
124         return nullptr;
125     }
126
127     IndexedDB::CursorDirection direction = IDBCursor::stringToDirection(directionString, ec);
128     if (ec)
129         return nullptr;
130
131     auto info = IDBCursorInfo::objectStoreCursor(m_transaction.get(), m_info.identifier(), range, direction);
132     Ref<IDBRequest> request = m_transaction->requestOpenCursor(*context, *this, info);
133     return WTF::move(request);
134 }
135
136 RefPtr<WebCore::IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, const String& direction, ExceptionCode& ec)
137 {
138     RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(context, key, ec);
139     if (ec)
140         return 0;
141
142     return openCursor(context, keyRange.get(), direction, ec);
143 }
144
145 RefPtr<WebCore::IDBRequest> IDBObjectStore::get(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec)
146 {
147     LOG(IndexedDB, "IDBObjectStore::get");
148
149     if (!context) {
150         ec = INVALID_STATE_ERR;
151         return nullptr;
152     }
153
154     if (!m_transaction->isActive()) {
155         ec = static_cast<ExceptionCode>(IDBExceptionCode::TransactionInactiveError);
156         return nullptr;
157     }
158
159     if (m_deleted) {
160         ec = INVALID_STATE_ERR;
161         return nullptr;
162     }
163
164     DOMRequestState requestState(context);
165     RefPtr<IDBKey> idbKey = scriptValueToIDBKey(&requestState, key);
166     if (!idbKey || idbKey->type() == KeyType::Invalid) {
167         ec = static_cast<ExceptionCode>(IDBExceptionCode::DataError);
168         return nullptr;
169     }
170
171     Ref<IDBRequest> request = m_transaction->requestGetRecord(*context, *this, idbKey.get());
172     return WTF::move(request);
173 }
174
175 RefPtr<WebCore::IDBRequest> IDBObjectStore::get(ScriptExecutionContext* context, IDBKeyRange* keyRange, ExceptionCode& ec)
176 {
177     LOG(IndexedDB, "IDBObjectStore::get");
178
179     if (!context) {
180         ec = INVALID_STATE_ERR;
181         return nullptr;
182     }
183
184     if (!m_transaction->isActive()) {
185         ec = static_cast<ExceptionCode>(IDBExceptionCode::TransactionInactiveError);
186         return nullptr;
187     }
188
189     if (m_deleted) {
190         ec = INVALID_STATE_ERR;
191         return nullptr;
192     }
193
194     IDBKeyRangeData keyRangeData(keyRange);
195     if (!keyRangeData.isValid()) {
196         ec = static_cast<ExceptionCode>(IDBExceptionCode::DataError);
197         return nullptr;
198     }
199
200     Ref<IDBRequest> request = m_transaction->requestGetRecord(*context, *this, keyRangeData);
201     return WTF::move(request);
202 }
203
204 RefPtr<WebCore::IDBRequest> IDBObjectStore::add(JSC::ExecState& state, JSC::JSValue value, ExceptionCode& ec)
205 {
206     return putOrAdd(state, value, nullptr, IndexedDB::ObjectStoreOverwriteMode::NoOverwrite, ec);
207 }
208
209 RefPtr<WebCore::IDBRequest> IDBObjectStore::add(JSC::ExecState& execState, JSC::JSValue value, JSC::JSValue key, ExceptionCode& ec)
210 {
211     auto idbKey = scriptValueToIDBKey(execState, key);
212     return putOrAdd(execState, value, idbKey, IndexedDB::ObjectStoreOverwriteMode::NoOverwrite, ec);
213 }
214
215 RefPtr<WebCore::IDBRequest> IDBObjectStore::put(JSC::ExecState& execState, JSC::JSValue value, JSC::JSValue key, ExceptionCode& ec)
216 {
217     auto idbKey = scriptValueToIDBKey(execState, key);
218     return putOrAdd(execState, value, idbKey, IndexedDB::ObjectStoreOverwriteMode::Overwrite, ec);
219 }
220
221 RefPtr<WebCore::IDBRequest> IDBObjectStore::put(JSC::ExecState& state, JSC::JSValue value, ExceptionCode& ec)
222 {
223     return putOrAdd(state, value, nullptr, IndexedDB::ObjectStoreOverwriteMode::Overwrite, ec);
224 }
225
226 RefPtr<WebCore::IDBRequest> IDBObjectStore::putOrAdd(JSC::ExecState& state, JSC::JSValue value, RefPtr<IDBKey> key, IndexedDB::ObjectStoreOverwriteMode overwriteMode, ExceptionCode& ec)
227 {
228     LOG(IndexedDB, "IDBObjectStore::putOrAdd");
229
230     if (m_transaction->isReadOnly()) {
231         ec = static_cast<ExceptionCode>(IDBExceptionCode::ReadOnlyError);
232         return nullptr;
233     }
234
235     if (!m_transaction->isActive()) {
236         ec = static_cast<ExceptionCode>(IDBExceptionCode::TransactionInactiveError);
237         return nullptr;
238     }
239
240     if (m_deleted) {
241         ec = INVALID_STATE_ERR;
242         return nullptr;
243     }
244
245     RefPtr<SerializedScriptValue> serializedValue = SerializedScriptValue::create(&state, value, nullptr, nullptr);
246     if (state.hadException()) {
247         ec = DATA_CLONE_ERR;
248         return nullptr;
249     }
250
251     if (serializedValue->hasBlobURLs()) {
252         // FIXME: Add Blob/File/FileList support
253         ec = DATA_CLONE_ERR;
254         return nullptr;
255     }
256
257     if (key && key->type() == KeyType::Invalid) {
258         ec = static_cast<ExceptionCode>(IDBExceptionCode::DataError);
259         return nullptr;
260     }
261
262     bool usesInlineKeys = !m_info.keyPath().isNull();
263     bool usesKeyGenerator = autoIncrement();
264     if (usesInlineKeys) {
265         if (key) {
266             ec = static_cast<ExceptionCode>(IDBExceptionCode::DataError);
267             return nullptr;
268         }
269
270         RefPtr<IDBKey> keyPathKey = maybeCreateIDBKeyFromScriptValueAndKeyPath(state, value, m_info.keyPath());
271         if (keyPathKey && !keyPathKey->isValid()) {
272             ec = static_cast<ExceptionCode>(IDBExceptionCode::DataError);
273             return nullptr;
274         }
275
276         if (!keyPathKey) {
277             if (usesKeyGenerator) {
278                 if (!canInjectIDBKeyIntoScriptValue(state, value, m_info.keyPath())) {
279                     ec = static_cast<ExceptionCode>(IDBExceptionCode::DataError);
280                     return nullptr;
281                 }
282             } else {
283                 ec = static_cast<ExceptionCode>(IDBExceptionCode::DataError);
284                 return nullptr;
285             }
286         }
287
288         if (keyPathKey) {
289             ASSERT(!key);
290             key = keyPathKey;
291         }
292     } else if (!usesKeyGenerator && !key) {
293         ec = static_cast<ExceptionCode>(IDBExceptionCode::DataError);
294         return nullptr;
295     }
296
297     auto context = scriptExecutionContextFromExecState(&state);
298     if (!context) {
299         ec = static_cast<ExceptionCode>(IDBExceptionCode::Unknown);
300         return nullptr;
301     }
302
303     Ref<IDBRequest> request = m_transaction->requestPutOrAdd(*context, *this, key.get(), *serializedValue, overwriteMode);
304     return adoptRef(request.leakRef());
305 }
306
307
308 RefPtr<WebCore::IDBRequest> IDBObjectStore::deleteFunction(ScriptExecutionContext* context, IDBKeyRange* keyRange, ExceptionCode& ec)
309 {
310     LOG(IndexedDB, "IDBObjectStore::deleteFunction");
311
312     if (m_transaction->isReadOnly()) {
313         ec = static_cast<ExceptionCode>(IDBExceptionCode::ReadOnlyError);
314         return nullptr;
315     }
316
317     if (!m_transaction->isActive()) {
318         ec = static_cast<ExceptionCode>(IDBExceptionCode::TransactionInactiveError);
319         return nullptr;
320     }
321
322     if (m_deleted) {
323         ec = INVALID_STATE_ERR;
324         return nullptr;
325     }
326
327     IDBKeyRangeData keyRangeData(keyRange);
328     if (!keyRangeData.isValid()) {
329         ec = static_cast<ExceptionCode>(IDBExceptionCode::DataError);
330         return nullptr;
331     }
332
333     Ref<IDBRequest> request = m_transaction->requestDeleteRecord(*context, *this, keyRangeData);
334     return WTF::move(request);
335 }
336
337 RefPtr<WebCore::IDBRequest> IDBObjectStore::deleteFunction(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec)
338 {
339     return deleteFunction(context, key.jsValue(), ec);
340 }
341
342 RefPtr<WebCore::IDBRequest> IDBObjectStore::deleteFunction(ScriptExecutionContext* context, JSC::JSValue key, ExceptionCode& ec)
343 {
344     DOMRequestState requestState(context);
345     RefPtr<IDBKey> idbKey = scriptValueToIDBKey(&requestState, key);
346     if (!idbKey || idbKey->type() == KeyType::Invalid) {
347         ec = static_cast<ExceptionCode>(IDBExceptionCode::DataError);
348         return nullptr;
349     }
350
351     return deleteFunction(context, &IDBKeyRange::create(idbKey.get()).get(), ec);
352 }
353
354 RefPtr<WebCore::IDBRequest> IDBObjectStore::clear(ScriptExecutionContext* context, ExceptionCode& ec)
355 {
356     LOG(IndexedDB, "IDBObjectStore::clear");
357
358     if (m_transaction->isReadOnly()) {
359         ec = static_cast<ExceptionCode>(IDBExceptionCode::ReadOnlyError);
360         return nullptr;
361     }
362
363     if (!m_transaction->isActive()) {
364         ec = static_cast<ExceptionCode>(IDBExceptionCode::TransactionInactiveError);
365         return nullptr;
366     }
367
368     if (m_deleted) {
369         ec = static_cast<ExceptionCode>(IDBExceptionCode::InvalidStateError);
370         return nullptr;
371     }
372
373     Ref<IDBRequest> request = m_transaction->requestClearObjectStore(*context, *this);
374     return adoptRef(request.leakRef());
375 }
376
377 RefPtr<WebCore::IDBIndex> IDBObjectStore::createIndex(ScriptExecutionContext* context, const String& name, const IDBKeyPath& keyPath, bool unique, bool multiEntry, ExceptionCode& ec)
378 {
379     LOG(IndexedDB, "IDBObjectStore::createIndex %s", name.utf8().data());
380
381     if (!context) {
382         ec = INVALID_STATE_ERR;
383         return nullptr;
384     }
385
386     if (m_deleted) {
387         ec = static_cast<ExceptionCode>(IDBExceptionCode::InvalidStateError);
388         return nullptr;
389     }
390
391     if (!m_transaction->isVersionChange()) {
392         ec = static_cast<ExceptionCode>(IDBExceptionCode::InvalidStateError);
393         return nullptr;
394     }
395
396     if (!m_transaction->isActive()) {
397         ec = static_cast<ExceptionCode>(IDBExceptionCode::TransactionInactiveError);
398         return nullptr;
399     }
400
401     if (!keyPath.isValid()) {
402         ec = IDBDatabaseException::SyntaxError;
403         return nullptr;
404     }
405
406     if (name.isNull()) {
407         ec = TypeError;
408         return nullptr;
409     }
410
411     if (m_info.hasIndex(name)) {
412         ec = IDBDatabaseException::ConstraintError;
413         return nullptr;
414     }
415
416     if (keyPath.type() == IndexedDB::KeyPathType::Array && multiEntry) {
417         ec = IDBDatabaseException::InvalidAccessError;
418         return nullptr;
419     }
420
421     // Install the new Index into the ObjectStore's info.
422     IDBIndexInfo info = m_info.createNewIndex(name, keyPath, unique, multiEntry);
423     m_transaction->database().didCreateIndexInfo(info);
424
425     // Create the actual IDBObjectStore from the transaction, which also schedules the operation server side.
426     Ref<IDBIndex> index = m_transaction->createIndex(*this, info);
427     m_referencedIndexes.set(name, &index.get());
428
429     return WTF::move(index);
430 }
431
432 RefPtr<WebCore::IDBIndex> IDBObjectStore::index(const String& indexName, ExceptionCode& ec)
433 {
434     LOG(IndexedDB, "IDBObjectStore::index");
435
436     if (indexName.isEmpty()) {
437         ec = NOT_FOUND_ERR;
438         return nullptr;
439     }
440
441     if (m_deleted) {
442         ec = INVALID_STATE_ERR;
443         return nullptr;
444     }
445
446     if (!m_transaction->isActive()) {
447         ec = static_cast<ExceptionCode>(IDBExceptionCode::TransactionInactiveError);
448         return nullptr;
449     }
450
451     auto iterator = m_referencedIndexes.find(indexName);
452     if (iterator != m_referencedIndexes.end())
453         return iterator->value;
454
455     auto* info = m_info.infoForExistingIndex(indexName);
456     if (!info) {
457         ec = NOT_FOUND_ERR;
458         return nullptr;
459     }
460
461     auto index = IDBIndex::create(*info, *this);
462     m_referencedIndexes.set(indexName, &index.get());
463
464     return WTF::move(index);
465 }
466
467 void IDBObjectStore::deleteIndex(const String& name, ExceptionCode& ec)
468 {
469     LOG(IndexedDB, "IDBObjectStore::deleteIndex %s", name.utf8().data());
470
471     if (m_deleted) {
472         ec = static_cast<ExceptionCode>(IDBExceptionCode::InvalidStateError);
473         return;
474     }
475
476     if (!m_transaction->isVersionChange()) {
477         ec = static_cast<ExceptionCode>(IDBExceptionCode::InvalidStateError);
478         return;
479     }
480
481     if (!m_transaction->isActive()) {
482         ec = static_cast<ExceptionCode>(IDBExceptionCode::TransactionInactiveError);
483         return;
484     }
485
486     if (!m_info.hasIndex(name)) {
487         ec = IDBDatabaseException::NotFoundError;
488         return;
489     }
490
491     auto* info = m_info.infoForExistingIndex(name);
492     ASSERT(info);
493     m_transaction->database().didDeleteIndexInfo(*info);
494
495     m_info.deleteIndex(name);
496
497     if (auto index = m_referencedIndexes.take(name))
498         index->markAsDeleted();
499
500     m_transaction->deleteIndex(m_info.identifier(), name);
501 }
502
503 RefPtr<WebCore::IDBRequest> IDBObjectStore::count(ScriptExecutionContext* context, ExceptionCode& ec)
504 {
505     LOG(IndexedDB, "IDBObjectStore::count");
506
507     if (!context) {
508         ec = INVALID_STATE_ERR;
509         return nullptr;
510     }
511
512     IDBKeyRangeData range;
513     range.isNull = false;
514     return doCount(*context, range, ec);
515 }
516
517 RefPtr<WebCore::IDBRequest> IDBObjectStore::count(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec)
518 {
519     LOG(IndexedDB, "IDBObjectStore::count");
520
521     if (!context) {
522         ec = INVALID_STATE_ERR;
523         return nullptr;
524     }
525
526     DOMRequestState requestState(context);
527     RefPtr<IDBKey> idbKey = scriptValueToIDBKey(&requestState, key);
528     if (!idbKey || idbKey->type() == KeyType::Invalid) {
529         ec = static_cast<ExceptionCode>(IDBExceptionCode::DataError);
530         return nullptr;
531     }
532
533     return doCount(*context, IDBKeyRangeData(idbKey.get()), ec);
534 }
535
536 RefPtr<WebCore::IDBRequest> IDBObjectStore::count(ScriptExecutionContext* context, IDBKeyRange* range, ExceptionCode& ec)
537 {
538     LOG(IndexedDB, "IDBObjectStore::count");
539
540     if (!context) {
541         ec = INVALID_STATE_ERR;
542         return nullptr;
543     }
544
545     return doCount(*context, IDBKeyRangeData(range), ec);
546 }
547
548 RefPtr<WebCore::IDBRequest> IDBObjectStore::doCount(ScriptExecutionContext& context, const IDBKeyRangeData& range, ExceptionCode& ec)
549 {
550     if (!m_transaction->isActive()) {
551         ec = static_cast<ExceptionCode>(IDBExceptionCode::TransactionInactiveError);
552         return nullptr;
553     }
554
555     if (m_deleted) {
556         ec = INVALID_STATE_ERR;
557         return nullptr;
558     }
559
560     if (range.isNull) {
561         ec = static_cast<ExceptionCode>(IDBExceptionCode::DataError);
562         return nullptr;
563     }
564
565     return m_transaction->requestCount(context, *this, range);
566 }
567
568 void IDBObjectStore::markAsDeleted()
569 {
570     m_deleted = true;
571 }
572
573 } // namespace IDBClient
574 } // namespace WebCore
575
576 #endif // ENABLE(INDEXED_DATABASE)