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 "modules/indexeddb/IDBCursor.h"
28
29 #include "bindings/v8/ExceptionState.h"
30 #include "bindings/v8/IDBBindingUtilities.h"
31 #include "bindings/v8/ScriptState.h"
32 #include "core/dom/ExceptionCode.h"
33 #include "core/dom/ExecutionContext.h"
34 #include "core/inspector/ScriptCallStack.h"
35 #include "modules/indexeddb/IDBAny.h"
36 #include "modules/indexeddb/IDBDatabase.h"
37 #include "modules/indexeddb/IDBObjectStore.h"
38 #include "modules/indexeddb/IDBTracing.h"
39 #include "modules/indexeddb/IDBTransaction.h"
40 #include "modules/indexeddb/WebIDBCallbacksImpl.h"
41 #include "public/platform/WebBlobInfo.h"
42 #include "public/platform/WebIDBDatabase.h"
43 #include "public/platform/WebIDBKeyRange.h"
44 #include <limits>
45
46 using blink::WebIDBCursor;
47 using blink::WebIDBDatabase;
48
49 namespace WebCore {
50
create(PassOwnPtr<blink::WebIDBCursor> backend,blink::WebIDBCursorDirection direction,IDBRequest * request,IDBAny * source,IDBTransaction * transaction)51 IDBCursor* IDBCursor::create(PassOwnPtr<blink::WebIDBCursor> backend, blink::WebIDBCursorDirection direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction)
52 {
53 return new IDBCursor(backend, direction, request, source, transaction);
54 }
55
directionNext()56 const AtomicString& IDBCursor::directionNext()
57 {
58 DEFINE_STATIC_LOCAL(AtomicString, next, ("next", AtomicString::ConstructFromLiteral));
59 return next;
60 }
61
directionNextUnique()62 const AtomicString& IDBCursor::directionNextUnique()
63 {
64 DEFINE_STATIC_LOCAL(AtomicString, nextunique, ("nextunique", AtomicString::ConstructFromLiteral));
65 return nextunique;
66 }
67
directionPrev()68 const AtomicString& IDBCursor::directionPrev()
69 {
70 DEFINE_STATIC_LOCAL(AtomicString, prev, ("prev", AtomicString::ConstructFromLiteral));
71 return prev;
72 }
73
directionPrevUnique()74 const AtomicString& IDBCursor::directionPrevUnique()
75 {
76 DEFINE_STATIC_LOCAL(AtomicString, prevunique, ("prevunique", AtomicString::ConstructFromLiteral));
77 return prevunique;
78 }
79
IDBCursor(PassOwnPtr<blink::WebIDBCursor> backend,blink::WebIDBCursorDirection direction,IDBRequest * request,IDBAny * source,IDBTransaction * transaction)80 IDBCursor::IDBCursor(PassOwnPtr<blink::WebIDBCursor> backend, blink::WebIDBCursorDirection direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction)
81 : m_backend(backend)
82 , m_request(request)
83 , m_direction(direction)
84 , m_source(source)
85 , m_transaction(transaction)
86 , m_gotValue(false)
87 , m_keyDirty(true)
88 , m_primaryKeyDirty(true)
89 , m_valueDirty(true)
90 {
91 ASSERT(m_backend);
92 ASSERT(m_request);
93 ASSERT(m_source->type() == IDBAny::IDBObjectStoreType || m_source->type() == IDBAny::IDBIndexType);
94 ASSERT(m_transaction);
95 ScriptWrappable::init(this);
96 }
97
~IDBCursor()98 IDBCursor::~IDBCursor()
99 {
100 handleBlobAcks();
101 }
102
trace(Visitor * visitor)103 void IDBCursor::trace(Visitor* visitor)
104 {
105 visitor->trace(m_request);
106 visitor->trace(m_source);
107 visitor->trace(m_transaction);
108 visitor->trace(m_key);
109 visitor->trace(m_primaryKey);
110 }
111
update(ScriptState * scriptState,ScriptValue & value,ExceptionState & exceptionState)112 IDBRequest* IDBCursor::update(ScriptState* scriptState, ScriptValue& value, ExceptionState& exceptionState)
113 {
114 IDB_TRACE("IDBCursor::update");
115
116 if (!m_gotValue) {
117 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage);
118 return 0;
119 }
120 if (isKeyCursor()) {
121 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::isKeyCursorErrorMessage);
122 return 0;
123 }
124 if (isDeleted()) {
125 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage);
126 return 0;
127 }
128 if (m_transaction->isFinished() || m_transaction->isFinishing()) {
129 exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
130 return 0;
131 }
132 if (!m_transaction->isActive()) {
133 exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
134 return 0;
135 }
136 if (m_transaction->isReadOnly()) {
137 exceptionState.throwDOMException(ReadOnlyError, "The record may not be updated inside a read-only transaction.");
138 return 0;
139 }
140
141 IDBObjectStore* objectStore = effectiveObjectStore();
142 const IDBKeyPath& keyPath = objectStore->metadata().keyPath;
143 const bool usesInLineKeys = !keyPath.isNull();
144 if (usesInLineKeys) {
145 IDBKey* keyPathKey = createIDBKeyFromScriptValueAndKeyPath(scriptState->isolate(), value, keyPath);
146 if (!keyPathKey || !keyPathKey->isEqual(m_primaryKey.get())) {
147 exceptionState.throwDOMException(DataError, "The effective object store of this cursor uses in-line keys and evaluating the key path of the value parameter results in a different value than the cursor's effective key.");
148 return 0;
149 }
150 }
151
152 return objectStore->put(scriptState, blink::WebIDBPutModeCursorUpdate, IDBAny::create(this), value, m_primaryKey, exceptionState);
153 }
154
advance(unsigned long count,ExceptionState & exceptionState)155 void IDBCursor::advance(unsigned long count, ExceptionState& exceptionState)
156 {
157 IDB_TRACE("IDBCursor::advance");
158 if (!count) {
159 exceptionState.throwTypeError("A count argument with value 0 (zero) was supplied, must be greater than 0.");
160 return;
161 }
162 if (!m_gotValue) {
163 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage);
164 return;
165 }
166 if (isDeleted()) {
167 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage);
168 return;
169 }
170
171 if (m_transaction->isFinished() || m_transaction->isFinishing()) {
172 exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
173 return;
174 }
175 if (!m_transaction->isActive()) {
176 exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
177 return;
178 }
179
180 m_request->setPendingCursor(this);
181 m_gotValue = false;
182 m_backend->advance(count, WebIDBCallbacksImpl::create(m_request).leakPtr());
183 }
184
continueFunction(ScriptState * scriptState,const ScriptValue & keyValue,ExceptionState & exceptionState)185 void IDBCursor::continueFunction(ScriptState* scriptState, const ScriptValue& keyValue, ExceptionState& exceptionState)
186 {
187 IDB_TRACE("IDBCursor::continue");
188 IDBKey* key = keyValue.isUndefined() || keyValue.isNull() ? nullptr : scriptValueToIDBKey(scriptState->isolate(), keyValue);
189 if (key && !key->isValid()) {
190 exceptionState.throwDOMException(DataError, IDBDatabase::notValidKeyErrorMessage);
191 return;
192 }
193 continueFunction(key, 0, exceptionState);
194 }
195
continuePrimaryKey(ScriptState * scriptState,const ScriptValue & keyValue,const ScriptValue & primaryKeyValue,ExceptionState & exceptionState)196 void IDBCursor::continuePrimaryKey(ScriptState* scriptState, const ScriptValue& keyValue, const ScriptValue& primaryKeyValue, ExceptionState& exceptionState)
197 {
198 IDB_TRACE("IDBCursor::continuePrimaryKey");
199 IDBKey* key = scriptValueToIDBKey(scriptState->isolate(), keyValue);
200 IDBKey* primaryKey = scriptValueToIDBKey(scriptState->isolate(), primaryKeyValue);
201 if (!key->isValid() || !primaryKey->isValid()) {
202 exceptionState.throwDOMException(DataError, IDBDatabase::notValidKeyErrorMessage);
203 return;
204 }
205 continueFunction(key, primaryKey, exceptionState);
206 }
207
continueFunction(IDBKey * key,IDBKey * primaryKey,ExceptionState & exceptionState)208 void IDBCursor::continueFunction(IDBKey* key, IDBKey* primaryKey, ExceptionState& exceptionState)
209 {
210 ASSERT(!primaryKey || (key && primaryKey));
211
212 if (m_transaction->isFinished() || m_transaction->isFinishing()) {
213 exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
214 return;
215 }
216 if (!m_transaction->isActive()) {
217 exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
218 return;
219 }
220
221 if (!m_gotValue) {
222 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage);
223 return;
224 }
225
226 if (isDeleted()) {
227 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage);
228 return;
229 }
230
231 if (key) {
232 ASSERT(m_key);
233 if (m_direction == blink::WebIDBCursorDirectionNext || m_direction == blink::WebIDBCursorDirectionNextNoDuplicate) {
234 const bool ok = m_key->isLessThan(key)
235 || (primaryKey && m_key->isEqual(key) && m_primaryKey->isLessThan(primaryKey));
236 if (!ok) {
237 exceptionState.throwDOMException(DataError, "The parameter is less than or equal to this cursor's position.");
238 return;
239 }
240
241 } else {
242 const bool ok = key->isLessThan(m_key.get())
243 || (primaryKey && key->isEqual(m_key.get()) && primaryKey->isLessThan(m_primaryKey.get()));
244 if (!ok) {
245 exceptionState.throwDOMException(DataError, "The parameter is greater than or equal to this cursor's position.");
246 return;
247 }
248 }
249 }
250
251 // FIXME: We're not using the context from when continue was called, which means the callback
252 // will be on the original context openCursor was called on. Is this right?
253 m_request->setPendingCursor(this);
254 m_gotValue = false;
255 m_backend->continueFunction(key, primaryKey, WebIDBCallbacksImpl::create(m_request).leakPtr());
256 }
257
deleteFunction(ScriptState * scriptState,ExceptionState & exceptionState)258 IDBRequest* IDBCursor::deleteFunction(ScriptState* scriptState, ExceptionState& exceptionState)
259 {
260 IDB_TRACE("IDBCursor::delete");
261 if (m_transaction->isFinished() || m_transaction->isFinishing()) {
262 exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
263 return 0;
264 }
265 if (!m_transaction->isActive()) {
266 exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
267 return 0;
268 }
269 if (m_transaction->isReadOnly()) {
270 exceptionState.throwDOMException(ReadOnlyError, "The record may not be deleted inside a read-only transaction.");
271 return 0;
272 }
273
274 if (!m_gotValue) {
275 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage);
276 return 0;
277 }
278 if (isKeyCursor()) {
279 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::isKeyCursorErrorMessage);
280 return 0;
281 }
282 if (isDeleted()) {
283 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage);
284 return 0;
285 }
286 if (!m_transaction->backendDB()) {
287 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::databaseClosedErrorMessage);
288 return 0;
289 }
290
291 IDBKeyRange* keyRange = IDBKeyRange::only(m_primaryKey, exceptionState);
292 ASSERT(!exceptionState.hadException());
293
294 IDBRequest* request = IDBRequest::create(scriptState, IDBAny::create(this), m_transaction.get());
295 m_transaction->backendDB()->deleteRange(m_transaction->id(), effectiveObjectStore()->id(), keyRange, WebIDBCallbacksImpl::create(request).leakPtr());
296 return request;
297 }
298
postSuccessHandlerCallback()299 void IDBCursor::postSuccessHandlerCallback()
300 {
301 if (m_backend)
302 m_backend->postSuccessHandlerCallback();
303 }
304
close()305 void IDBCursor::close()
306 {
307 handleBlobAcks();
308 m_request.clear();
309 m_backend.clear();
310 }
311
key(ScriptState * scriptState)312 ScriptValue IDBCursor::key(ScriptState* scriptState)
313 {
314 m_keyDirty = false;
315 return idbKeyToScriptValue(scriptState, m_key);
316 }
317
primaryKey(ScriptState * scriptState)318 ScriptValue IDBCursor::primaryKey(ScriptState* scriptState)
319 {
320 m_primaryKeyDirty = false;
321 return idbKeyToScriptValue(scriptState, m_primaryKey);
322 }
323
value(ScriptState * scriptState)324 ScriptValue IDBCursor::value(ScriptState* scriptState)
325 {
326 ASSERT(isCursorWithValue());
327
328 IDBObjectStore* objectStore = effectiveObjectStore();
329 const IDBObjectStoreMetadata& metadata = objectStore->metadata();
330 IDBAny* value;
331 if (metadata.autoIncrement && !metadata.keyPath.isNull()) {
332 value = IDBAny::create(m_value, m_blobInfo.get(), m_primaryKey, metadata.keyPath);
333 #ifndef NDEBUG
334 assertPrimaryKeyValidOrInjectable(scriptState, m_value, m_blobInfo.get(), m_primaryKey, metadata.keyPath);
335 #endif
336 } else {
337 value = IDBAny::create(m_value, m_blobInfo.get());
338 }
339
340 m_valueDirty = false;
341 ScriptValue scriptValue = idbAnyToScriptValue(scriptState, value);
342 handleBlobAcks();
343 return scriptValue;
344 }
345
source(ScriptState * scriptState) const346 ScriptValue IDBCursor::source(ScriptState* scriptState) const
347 {
348 return idbAnyToScriptValue(scriptState, m_source);
349 }
350
setValueReady(IDBKey * key,IDBKey * primaryKey,PassRefPtr<SharedBuffer> value,PassOwnPtr<Vector<blink::WebBlobInfo>> blobInfo)351 void IDBCursor::setValueReady(IDBKey* key, IDBKey* primaryKey, PassRefPtr<SharedBuffer> value, PassOwnPtr<Vector<blink::WebBlobInfo> > blobInfo)
352 {
353 m_key = key;
354 m_keyDirty = true;
355
356 m_primaryKey = primaryKey;
357 m_primaryKeyDirty = true;
358
359 if (isCursorWithValue()) {
360 m_value = value;
361 handleBlobAcks();
362 m_blobInfo = blobInfo;
363 m_valueDirty = true;
364 }
365
366 m_gotValue = true;
367 }
368
effectiveObjectStore() const369 IDBObjectStore* IDBCursor::effectiveObjectStore() const
370 {
371 if (m_source->type() == IDBAny::IDBObjectStoreType)
372 return m_source->idbObjectStore();
373 return m_source->idbIndex()->objectStore();
374 }
375
isDeleted() const376 bool IDBCursor::isDeleted() const
377 {
378 if (m_source->type() == IDBAny::IDBObjectStoreType)
379 return m_source->idbObjectStore()->isDeleted();
380 return m_source->idbIndex()->isDeleted();
381 }
382
handleBlobAcks()383 void IDBCursor::handleBlobAcks()
384 {
385 ASSERT(m_request || !m_blobInfo || !m_blobInfo->size());
386 if (m_blobInfo.get() && m_blobInfo->size()) {
387 ASSERT(m_request);
388 m_transaction->db()->ackReceivedBlobs(m_blobInfo.get());
389 m_blobInfo.clear();
390 }
391 }
392
stringToDirection(const String & directionString,ExceptionState & exceptionState)393 blink::WebIDBCursorDirection IDBCursor::stringToDirection(const String& directionString, ExceptionState& exceptionState)
394 {
395 if (directionString.isNull() || directionString == IDBCursor::directionNext())
396 return blink::WebIDBCursorDirectionNext;
397 if (directionString == IDBCursor::directionNextUnique())
398 return blink::WebIDBCursorDirectionNextNoDuplicate;
399 if (directionString == IDBCursor::directionPrev())
400 return blink::WebIDBCursorDirectionPrev;
401 if (directionString == IDBCursor::directionPrevUnique())
402 return blink::WebIDBCursorDirectionPrevNoDuplicate;
403
404 exceptionState.throwTypeError("The direction provided ('" + directionString + "') is not one of 'next', 'nextunique', 'prev', or 'prevunique'.");
405 return blink::WebIDBCursorDirectionNext;
406 }
407
directionToString(unsigned short direction)408 const AtomicString& IDBCursor::directionToString(unsigned short direction)
409 {
410 switch (direction) {
411 case blink::WebIDBCursorDirectionNext:
412 return IDBCursor::directionNext();
413
414 case blink::WebIDBCursorDirectionNextNoDuplicate:
415 return IDBCursor::directionNextUnique();
416
417 case blink::WebIDBCursorDirectionPrev:
418 return IDBCursor::directionPrev();
419
420 case blink::WebIDBCursorDirectionPrevNoDuplicate:
421 return IDBCursor::directionPrevUnique();
422
423 default:
424 ASSERT_NOT_REACHED();
425 return IDBCursor::directionNext();
426 }
427 }
428
429 } // namespace WebCore
430