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