[Chromium] IndexedDB: Assertion failure when storing File objects
[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 {
51     ASSERT(m_backend);
52     ASSERT(m_transaction);
53     // We pass a reference to this object before it can be adopted.
54     relaxAdoptionRequirement();
55 }
56
57 String IDBObjectStore::name() const
58 {
59     IDB_TRACE("IDBObjectStore::name");
60     return m_backend->name();
61 }
62
63 String IDBObjectStore::keyPath() const
64 {
65     IDB_TRACE("IDBObjectStore::keyPath");
66     return m_backend->keyPath();
67 }
68
69 PassRefPtr<DOMStringList> IDBObjectStore::indexNames() const
70 {
71     IDB_TRACE("IDBObjectStore::indexNames");
72     return m_backend->indexNames();
73 }
74
75 IDBTransaction* IDBObjectStore::transaction() const
76 {
77     IDB_TRACE("IDBObjectStore::transaction");
78     return m_transaction.get();
79 }
80
81 PassRefPtr<IDBRequest> IDBObjectStore::get(ScriptExecutionContext* context, PassRefPtr<IDBKey> key, ExceptionCode& ec)
82 {
83     IDB_TRACE("IDBObjectStore::get");
84     if (key && (key->type() == IDBKey::InvalidType)) {
85         ec = IDBDatabaseException::DATA_ERR;
86         return 0;
87     }
88
89     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
90     m_backend->get(key, request, m_transaction->backend(), ec);
91     if (ec) {
92         request->markEarlyDeath();
93         return 0;
94     }
95     return request.release();
96 }
97
98 PassRefPtr<IDBRequest> IDBObjectStore::add(ScriptExecutionContext* context, PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBKey> key, ExceptionCode& ec)
99 {
100     IDB_TRACE("IDBObjectStore::add");
101     if (key && (key->type() == IDBKey::InvalidType)) {
102         ec = IDBDatabaseException::DATA_ERR;
103         return 0;
104     }
105
106     RefPtr<SerializedScriptValue> value = prpValue;
107     if (value->blobURLs().size() > 0) {
108         // FIXME: Add Blob/File/FileList support
109         ec = DATA_CLONE_ERR;
110         return 0;
111     }
112
113     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
114     m_backend->put(value, key, IDBObjectStoreBackendInterface::AddOnly, request, m_transaction->backend(), ec);
115     if (ec) {
116         request->markEarlyDeath();
117         return 0;
118     }
119     return request.release();
120 }
121
122 PassRefPtr<IDBRequest> IDBObjectStore::put(ScriptExecutionContext* context, PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBKey> key, ExceptionCode& ec)
123 {
124     IDB_TRACE("IDBObjectStore::put");
125     if (key && (key->type() == IDBKey::InvalidType)) {
126         ec = IDBDatabaseException::DATA_ERR;
127         return 0;
128     }
129
130     RefPtr<SerializedScriptValue> value = prpValue;
131     if (value->blobURLs().size() > 0) {
132         // FIXME: Add Blob/File/FileList support
133         ec = DATA_CLONE_ERR;
134         return 0;
135     }
136
137     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
138     m_backend->put(value, key, IDBObjectStoreBackendInterface::AddOrUpdate, request, m_transaction->backend(), ec);
139     if (ec) {
140         request->markEarlyDeath();
141         return 0;
142     }
143     return request.release();
144 }
145
146 PassRefPtr<IDBRequest> IDBObjectStore::deleteFunction(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> keyRange, ExceptionCode& ec)
147 {
148     IDB_TRACE("IDBObjectStore::delete");
149     if (!keyRange) {
150         ec = IDBDatabaseException::DATA_ERR;
151         return 0;
152     }
153
154     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
155     m_backend->deleteFunction(keyRange, request, m_transaction->backend(), ec);
156     if (ec) {
157         request->markEarlyDeath();
158         return 0;
159     }
160     return request.release();
161 }
162
163 PassRefPtr<IDBRequest> IDBObjectStore::deleteFunction(ScriptExecutionContext* context, PassRefPtr<IDBKey> key, ExceptionCode& ec)
164 {
165     IDB_TRACE("IDBObjectStore::delete");
166     if (!key || !key->valid()) {
167         ec = IDBDatabaseException::DATA_ERR;
168         return 0;
169     }
170
171     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
172     m_backend->deleteFunction(key, request, m_transaction->backend(), ec);
173     if (ec) {
174         request->markEarlyDeath();
175         return 0;
176     }
177     return request.release();
178 }
179
180 PassRefPtr<IDBRequest> IDBObjectStore::clear(ScriptExecutionContext* context, ExceptionCode& ec)
181 {
182     IDB_TRACE("IDBObjectStore::clear");
183     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
184     m_backend->clear(request, m_transaction->backend(), ec);
185     if (ec) {
186         request->markEarlyDeath();
187         return 0;
188     }
189     return request.release();
190 }
191
192 PassRefPtr<IDBIndex> IDBObjectStore::createIndex(const String& name, const String& keyPath, const Dictionary& options, ExceptionCode& ec)
193 {
194     IDB_TRACE("IDBObjectStore::createIndex");
195     if (!IDBIsValidKeyPath(keyPath)) {
196         ec = IDBDatabaseException::NON_TRANSIENT_ERR;
197         return 0;
198     }
199
200     bool unique = false;
201     options.get("unique", unique);
202
203     bool multiEntry = false;
204     options.get("multiEntry", multiEntry);
205
206     // FIXME: When Array-type keyPaths are supported, throw exception if keyPath is Array and multiEntry is true.
207
208     RefPtr<IDBIndexBackendInterface> indexBackend = m_backend->createIndex(name, keyPath, unique, multiEntry, m_transaction->backend(), ec);
209     ASSERT(!indexBackend != !ec); // If we didn't get an index, we should have gotten an exception code. And vice versa.
210     if (!indexBackend)
211         return 0;
212     RefPtr<IDBIndex> index = IDBIndex::create(indexBackend.release(), this, m_transaction.get());
213     m_indexMap.set(name, index);
214     return index.release();
215 }
216
217 PassRefPtr<IDBIndex> IDBObjectStore::index(const String& name, ExceptionCode& ec)
218 {
219     IDB_TRACE("IDBObjectStore::index");
220     if (m_transaction->finished()) {
221         ec = IDBDatabaseException::NOT_ALLOWED_ERR;
222         return 0;
223     }
224
225     IDBIndexMap::iterator it = m_indexMap.find(name);
226     if (it != m_indexMap.end())
227         return it->second;
228
229     RefPtr<IDBIndexBackendInterface> indexBackend = m_backend->index(name, ec);
230     ASSERT(!indexBackend != !ec); // If we didn't get an index, we should have gotten an exception code. And vice versa.
231     if (!indexBackend)
232         return 0;
233
234     RefPtr<IDBIndex> index = IDBIndex::create(indexBackend.release(), this, m_transaction.get());
235     m_indexMap.set(name, index);
236     return index.release();
237 }
238
239 void IDBObjectStore::deleteIndex(const String& name, ExceptionCode& ec)
240 {
241     m_backend->deleteIndex(name, m_transaction->backend(), ec);
242 }
243
244 PassRefPtr<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> range, unsigned short direction, ExceptionCode& ec)
245 {
246     IDB_TRACE("IDBObjectStore::openCursor");
247     if (direction != IDBCursor::NEXT && direction != IDBCursor::NEXT_NO_DUPLICATE && direction != IDBCursor::PREV && direction != IDBCursor::PREV_NO_DUPLICATE) {
248         // FIXME: May need to change when specced: http://www.w3.org/Bugs/Public/show_bug.cgi?id=11406
249         ec = IDBDatabaseException::CONSTRAINT_ERR;
250         return 0;
251     }
252
253     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
254     request->setCursorType(IDBCursorBackendInterface::ObjectStoreCursor);
255     m_backend->openCursor(range, direction, request, m_transaction->backend(), ec);
256     if (ec) {
257         request->markEarlyDeath();
258         return 0;
259     }
260     return request.release();
261 }
262
263 PassRefPtr<IDBRequest> IDBObjectStore::count(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> range, ExceptionCode& ec)
264 {
265     IDB_TRACE("IDBObjectStore::count");
266     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
267     m_backend->count(range, request, m_transaction->backend(), ec);
268     if (ec) {
269         request->markEarlyDeath();
270         return 0;
271     }
272     return request.release();
273 }
274
275 PassRefPtr<IDBRequest> IDBObjectStore::count(ScriptExecutionContext* context, PassRefPtr<IDBKey> key, ExceptionCode& ec)
276 {
277     IDB_TRACE("IDBObjectStore::count");
278     RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(key, ec);
279     if (ec)
280         return 0;
281     return count(context, keyRange.release(), ec);
282 }
283
284 void IDBObjectStore::transactionFinished()
285 {
286     ASSERT(m_transaction->finished());
287
288     // Break reference cycles.
289     m_indexMap.clear();
290 }
291
292
293 } // namespace WebCore
294
295 #endif // ENABLE(INDEXED_DATABASE)