Modern IDB: Pipe through cursor functions from client to server.
[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     RELEASE_ASSERT_NOT_REACHED();
81 }
82
83 RefPtr<WebCore::IDBTransaction> IDBObjectStore::transaction()
84 {
85     return &m_transaction.get();
86 }
87
88 bool IDBObjectStore::autoIncrement() const
89 {
90     return m_info.autoIncrement();
91 }
92
93 RefPtr<WebCore::IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, ExceptionCode& ec)
94 {
95     return openCursor(context, static_cast<IDBKeyRange*>(nullptr), ec);
96 }
97
98 RefPtr<WebCore::IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, IDBKeyRange* keyRange, ExceptionCode& ec)
99 {
100     return openCursor(context, keyRange, IDBCursor::directionNext(), ec);
101 }
102
103 RefPtr<WebCore::IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec)
104 {
105     return openCursor(context, key, IDBCursor::directionNext(), ec);
106 }
107
108 RefPtr<WebCore::IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, IDBKeyRange* range, const String& directionString, ExceptionCode& ec)
109 {
110     LOG(IndexedDB, "IDBObjectStore::openCursor");
111
112     if (m_deleted) {
113         ec = IDBDatabaseException::InvalidStateError;
114         return nullptr;
115     }
116
117     if (!m_transaction->isActive()) {
118         ec = IDBDatabaseException::TransactionInactiveError;
119         return nullptr;
120     }
121
122     IndexedDB::CursorDirection direction = IDBCursor::stringToDirection(directionString, ec);
123     if (ec)
124         return nullptr;
125
126     auto info = IDBCursorInfo::objectStoreCursor(m_transaction.get(), m_info.identifier(), range, direction);
127     Ref<IDBRequest> request = m_transaction->requestOpenCursor(*context, *this, info);
128     return WTF::move(request);
129 }
130
131 RefPtr<WebCore::IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, const String& direction, ExceptionCode& ec)
132 {
133     RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(context, key, ec);
134     if (ec)
135         return 0;
136
137     return openCursor(context, keyRange.get(), direction, ec);
138 }
139
140 RefPtr<WebCore::IDBRequest> IDBObjectStore::get(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec)
141 {
142     LOG(IndexedDB, "IDBObjectStore::get");
143
144     if (!context) {
145         ec = INVALID_STATE_ERR;
146         return nullptr;
147     }
148
149     if (!m_transaction->isActive()) {
150         ec = static_cast<ExceptionCode>(IDBExceptionCode::TransactionInactiveError);
151         return nullptr;
152     }
153
154     if (m_deleted) {
155         ec = INVALID_STATE_ERR;
156         return nullptr;
157     }
158
159     DOMRequestState requestState(context);
160     RefPtr<IDBKey> idbKey = scriptValueToIDBKey(&requestState, key);
161     if (!idbKey || idbKey->type() == KeyType::Invalid) {
162         ec = static_cast<ExceptionCode>(IDBExceptionCode::DataError);
163         return nullptr;
164     }
165
166     Ref<IDBRequest> request = m_transaction->requestGetRecord(*context, *this, idbKey.get());
167     return WTF::move(request);
168 }
169
170 RefPtr<WebCore::IDBRequest> IDBObjectStore::get(ScriptExecutionContext* context, IDBKeyRange* keyRange, ExceptionCode& ec)
171 {
172     LOG(IndexedDB, "IDBObjectStore::get");
173
174     if (!context) {
175         ec = INVALID_STATE_ERR;
176         return nullptr;
177     }
178
179     if (!m_transaction->isActive()) {
180         ec = static_cast<ExceptionCode>(IDBExceptionCode::TransactionInactiveError);
181         return nullptr;
182     }
183
184     if (m_deleted) {
185         ec = INVALID_STATE_ERR;
186         return nullptr;
187     }
188
189     IDBKeyRangeData keyRangeData(keyRange);
190     if (!keyRangeData.isValid()) {
191         ec = static_cast<ExceptionCode>(IDBExceptionCode::DataError);
192         return nullptr;
193     }
194
195     Ref<IDBRequest> request = m_transaction->requestGetRecord(*context, *this, keyRangeData);
196     return WTF::move(request);
197 }
198
199 RefPtr<WebCore::IDBRequest> IDBObjectStore::add(JSC::ExecState& state, JSC::JSValue value, ExceptionCode& ec)
200 {
201     return putOrAdd(state, value, nullptr, IndexedDB::ObjectStoreOverwriteMode::NoOverwrite, ec);
202 }
203
204 RefPtr<WebCore::IDBRequest> IDBObjectStore::add(JSC::ExecState& execState, JSC::JSValue value, JSC::JSValue key, ExceptionCode& ec)
205 {
206     auto idbKey = scriptValueToIDBKey(execState, key);
207     return putOrAdd(execState, value, idbKey, IndexedDB::ObjectStoreOverwriteMode::NoOverwrite, ec);
208 }
209
210 RefPtr<WebCore::IDBRequest> IDBObjectStore::put(JSC::ExecState& execState, JSC::JSValue value, JSC::JSValue key, ExceptionCode& ec)
211 {
212     auto idbKey = scriptValueToIDBKey(execState, key);
213     return putOrAdd(execState, value, idbKey, IndexedDB::ObjectStoreOverwriteMode::Overwrite, ec);
214 }
215
216 RefPtr<WebCore::IDBRequest> IDBObjectStore::put(JSC::ExecState& state, JSC::JSValue value, ExceptionCode& ec)
217 {
218     return putOrAdd(state, value, nullptr, IndexedDB::ObjectStoreOverwriteMode::Overwrite, ec);
219 }
220
221 RefPtr<WebCore::IDBRequest> IDBObjectStore::putOrAdd(JSC::ExecState& state, JSC::JSValue value, RefPtr<IDBKey> key, IndexedDB::ObjectStoreOverwriteMode overwriteMode, ExceptionCode& ec)
222 {
223     LOG(IndexedDB, "IDBObjectStore::putOrAdd");
224
225     if (m_transaction->isReadOnly()) {
226         ec = static_cast<ExceptionCode>(IDBExceptionCode::ReadOnlyError);
227         return nullptr;
228     }
229
230     if (!m_transaction->isActive()) {
231         ec = static_cast<ExceptionCode>(IDBExceptionCode::TransactionInactiveError);
232         return nullptr;
233     }
234
235     if (m_deleted) {
236         ec = INVALID_STATE_ERR;
237         return nullptr;
238     }
239
240     RefPtr<SerializedScriptValue> serializedValue = SerializedScriptValue::create(&state, value, nullptr, nullptr);
241     if (state.hadException()) {
242         ec = DATA_CLONE_ERR;
243         return nullptr;
244     }
245
246     if (serializedValue->hasBlobURLs()) {
247         // FIXME: Add Blob/File/FileList support
248         ec = DATA_CLONE_ERR;
249         return nullptr;
250     }
251
252     if (key && key->type() == KeyType::Invalid) {
253         ec = static_cast<ExceptionCode>(IDBExceptionCode::DataError);
254         return nullptr;
255     }
256
257     bool usesInlineKeys = !m_info.keyPath().isNull();
258     bool usesKeyGenerator = autoIncrement();
259     if (usesInlineKeys) {
260         if (key) {
261             ec = static_cast<ExceptionCode>(IDBExceptionCode::DataError);
262             return nullptr;
263         }
264
265         RefPtr<IDBKey> keyPathKey = maybeCreateIDBKeyFromScriptValueAndKeyPath(state, value, m_info.keyPath());
266         if (keyPathKey && !keyPathKey->isValid()) {
267             ec = static_cast<ExceptionCode>(IDBExceptionCode::DataError);
268             return nullptr;
269         }
270
271         if (!keyPathKey) {
272             if (usesKeyGenerator) {
273                 if (!canInjectIDBKeyIntoScriptValue(state, value, m_info.keyPath())) {
274                     ec = static_cast<ExceptionCode>(IDBExceptionCode::DataError);
275                     return nullptr;
276                 }
277             } else {
278                 ec = static_cast<ExceptionCode>(IDBExceptionCode::DataError);
279                 return nullptr;
280             }
281         }
282
283         if (keyPathKey) {
284             ASSERT(!key);
285             key = keyPathKey;
286         }
287     } else if (!usesKeyGenerator && !key) {
288         ec = static_cast<ExceptionCode>(IDBExceptionCode::DataError);
289         return nullptr;
290     }
291
292     auto context = scriptExecutionContextFromExecState(&state);
293     if (!context) {
294         ec = static_cast<ExceptionCode>(IDBExceptionCode::Unknown);
295         return nullptr;
296     }
297
298     Ref<IDBRequest> request = m_transaction->requestPutOrAdd(*context, *this, key.get(), *serializedValue, overwriteMode);
299     return adoptRef(request.leakRef());
300 }
301
302
303 RefPtr<WebCore::IDBRequest> IDBObjectStore::deleteFunction(ScriptExecutionContext* context, IDBKeyRange* keyRange, ExceptionCode& ec)
304 {
305     LOG(IndexedDB, "IDBObjectStore::deleteFunction");
306
307     if (m_transaction->isReadOnly()) {
308         ec = static_cast<ExceptionCode>(IDBExceptionCode::ReadOnlyError);
309         return nullptr;
310     }
311
312     if (!m_transaction->isActive()) {
313         ec = static_cast<ExceptionCode>(IDBExceptionCode::TransactionInactiveError);
314         return nullptr;
315     }
316
317     if (m_deleted) {
318         ec = INVALID_STATE_ERR;
319         return nullptr;
320     }
321
322     IDBKeyRangeData keyRangeData(keyRange);
323     if (!keyRangeData.isValid()) {
324         ec = static_cast<ExceptionCode>(IDBExceptionCode::DataError);
325         return nullptr;
326     }
327
328     Ref<IDBRequest> request = m_transaction->requestDeleteRecord(*context, *this, keyRangeData);
329     return WTF::move(request);
330 }
331
332 RefPtr<WebCore::IDBRequest> IDBObjectStore::deleteFunction(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec)
333 {
334     return deleteFunction(context, key.jsValue(), ec);
335 }
336
337 RefPtr<WebCore::IDBRequest> IDBObjectStore::deleteFunction(ScriptExecutionContext* context, JSC::JSValue key, ExceptionCode& ec)
338 {
339     DOMRequestState requestState(context);
340     RefPtr<IDBKey> idbKey = scriptValueToIDBKey(&requestState, key);
341     if (!idbKey || idbKey->type() == KeyType::Invalid) {
342         ec = static_cast<ExceptionCode>(IDBExceptionCode::DataError);
343         return nullptr;
344     }
345
346     return deleteFunction(context, &IDBKeyRange::create(idbKey.get()).get(), ec);
347 }
348
349 RefPtr<WebCore::IDBRequest> IDBObjectStore::clear(ScriptExecutionContext* context, ExceptionCode& ec)
350 {
351     LOG(IndexedDB, "IDBObjectStore::clear");
352
353     if (m_transaction->isReadOnly()) {
354         ec = static_cast<ExceptionCode>(IDBExceptionCode::ReadOnlyError);
355         return nullptr;
356     }
357
358     if (!m_transaction->isActive()) {
359         ec = static_cast<ExceptionCode>(IDBExceptionCode::TransactionInactiveError);
360         return nullptr;
361     }
362
363     if (m_deleted) {
364         ec = static_cast<ExceptionCode>(IDBExceptionCode::InvalidStateError);
365         return nullptr;
366     }
367
368     Ref<IDBRequest> request = m_transaction->requestClearObjectStore(*context, *this);
369     return adoptRef(request.leakRef());
370 }
371
372 RefPtr<WebCore::IDBIndex> IDBObjectStore::createIndex(ScriptExecutionContext* context, const String& name, const IDBKeyPath& keyPath, bool unique, bool multiEntry, ExceptionCode& ec)
373 {
374     LOG(IndexedDB, "IDBObjectStore::createIndex %s", name.utf8().data());
375
376     if (!context) {
377         ec = INVALID_STATE_ERR;
378         return nullptr;
379     }
380
381     if (m_deleted) {
382         ec = static_cast<ExceptionCode>(IDBExceptionCode::InvalidStateError);
383         return nullptr;
384     }
385
386     if (!m_transaction->isVersionChange()) {
387         ec = static_cast<ExceptionCode>(IDBExceptionCode::InvalidStateError);
388         return nullptr;
389     }
390
391     if (!m_transaction->isActive()) {
392         ec = static_cast<ExceptionCode>(IDBExceptionCode::TransactionInactiveError);
393         return nullptr;
394     }
395
396     if (!keyPath.isValid()) {
397         ec = IDBDatabaseException::SyntaxError;
398         return nullptr;
399     }
400
401     if (name.isNull()) {
402         ec = TypeError;
403         return nullptr;
404     }
405
406     if (m_info.hasIndex(name)) {
407         ec = IDBDatabaseException::ConstraintError;
408         return nullptr;
409     }
410
411     if (keyPath.type() == IndexedDB::KeyPathType::Array && multiEntry) {
412         ec = IDBDatabaseException::InvalidAccessError;
413         return nullptr;
414     }
415
416     // Install the new Index into the ObjectStore's info.
417     IDBIndexInfo info = m_info.createNewIndex(name, keyPath, unique, multiEntry);
418     m_transaction->database().didCreateIndexInfo(info);
419
420     // Create the actual IDBObjectStore from the transaction, which also schedules the operation server side.
421     Ref<IDBIndex> index = m_transaction->createIndex(*this, info);
422     return WTF::move(index);
423 }
424
425 RefPtr<WebCore::IDBIndex> IDBObjectStore::index(const String& indexName, ExceptionCode& ec)
426 {
427     LOG(IndexedDB, "IDBObjectStore::index");
428
429     if (indexName.isEmpty()) {
430         ec = NOT_FOUND_ERR;
431         return nullptr;
432     }
433
434     if (m_deleted) {
435         ec = INVALID_STATE_ERR;
436         return nullptr;
437     }
438
439     auto iterator = m_referencedIndexes.find(indexName);
440     if (iterator != m_referencedIndexes.end())
441         return iterator->value;
442
443     auto* info = m_info.infoForExistingIndex(indexName);
444     if (!info) {
445         ec = NOT_FOUND_ERR;
446         return nullptr;
447     }
448
449     auto index = IDBIndex::create(*info, *this);
450     m_referencedIndexes.set(indexName, &index.get());
451
452     return WTF::move(index);
453 }
454
455 void IDBObjectStore::deleteIndex(const String&, ExceptionCode&)
456 {
457     RELEASE_ASSERT_NOT_REACHED();
458 }
459
460 RefPtr<WebCore::IDBRequest> IDBObjectStore::count(ScriptExecutionContext* context, ExceptionCode& ec)
461 {
462     LOG(IndexedDB, "IDBObjectStore::count");
463
464     if (!context) {
465         ec = INVALID_STATE_ERR;
466         return nullptr;
467     }
468
469     IDBKeyRangeData range;
470     range.isNull = false;
471     return doCount(*context, range, ec);
472 }
473
474 RefPtr<WebCore::IDBRequest> IDBObjectStore::count(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec)
475 {
476     LOG(IndexedDB, "IDBObjectStore::count");
477
478     if (!context) {
479         ec = INVALID_STATE_ERR;
480         return nullptr;
481     }
482
483     DOMRequestState requestState(context);
484     RefPtr<IDBKey> idbKey = scriptValueToIDBKey(&requestState, key);
485     if (!idbKey || idbKey->type() == KeyType::Invalid) {
486         ec = static_cast<ExceptionCode>(IDBExceptionCode::DataError);
487         return nullptr;
488     }
489
490     return doCount(*context, IDBKeyRangeData(idbKey.get()), ec);
491 }
492
493 RefPtr<WebCore::IDBRequest> IDBObjectStore::count(ScriptExecutionContext* context, IDBKeyRange* range, ExceptionCode& ec)
494 {
495     LOG(IndexedDB, "IDBObjectStore::count");
496
497     if (!context) {
498         ec = INVALID_STATE_ERR;
499         return nullptr;
500     }
501
502     return doCount(*context, IDBKeyRangeData(range), ec);
503 }
504
505 RefPtr<WebCore::IDBRequest> IDBObjectStore::doCount(ScriptExecutionContext& context, const IDBKeyRangeData& range, ExceptionCode& ec)
506 {
507     if (!m_transaction->isActive()) {
508         ec = static_cast<ExceptionCode>(IDBExceptionCode::TransactionInactiveError);
509         return nullptr;
510     }
511
512     if (m_deleted) {
513         ec = INVALID_STATE_ERR;
514         return nullptr;
515     }
516
517     if (range.isNull) {
518         ec = static_cast<ExceptionCode>(IDBExceptionCode::DataError);
519         return nullptr;
520     }
521
522     return m_transaction->requestCount(context, *this, range);
523 }
524
525 void IDBObjectStore::markAsDeleted()
526 {
527     m_deleted = true;
528 }
529
530 } // namespace IDBClient
531 } // namespace WebCore
532
533 #endif // ENABLE(INDEXED_DATABASE)