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