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