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/ScriptExecutionContext.h" 33 #include "core/inspector/ScriptCallStack.h" 34 #include "modules/indexeddb/IDBAny.h" 35 #include "modules/indexeddb/IDBCallbacks.h" 36 #include "modules/indexeddb/IDBCursorBackendInterface.h" 37 #include "modules/indexeddb/IDBKey.h" 38 #include "modules/indexeddb/IDBObjectStore.h" 39 #include "modules/indexeddb/IDBRequest.h" 40 #include "modules/indexeddb/IDBTracing.h" 41 #include "modules/indexeddb/IDBTransaction.h" 42 #include <limits> 43 44 namespace WebCore { 45 46 PassRefPtr<IDBCursor> IDBCursor::create(PassRefPtr<IDBCursorBackendInterface> backend, IndexedDB::CursorDirection direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction) 47 { 48 return adoptRef(new IDBCursor(backend, direction, request, source, transaction)); 49 } 50 51 const AtomicString& IDBCursor::directionNext() 52 { 53 DEFINE_STATIC_LOCAL(AtomicString, next, ("next", AtomicString::ConstructFromLiteral)); 54 return next; 55 } 56 57 const AtomicString& IDBCursor::directionNextUnique() 58 { 59 DEFINE_STATIC_LOCAL(AtomicString, nextunique, ("nextunique", AtomicString::ConstructFromLiteral)); 60 return nextunique; 61 } 62 63 const AtomicString& IDBCursor::directionPrev() 64 { 65 DEFINE_STATIC_LOCAL(AtomicString, prev, ("prev", AtomicString::ConstructFromLiteral)); 66 return prev; 67 } 68 69 const AtomicString& IDBCursor::directionPrevUnique() 70 { 71 DEFINE_STATIC_LOCAL(AtomicString, prevunique, ("prevunique", AtomicString::ConstructFromLiteral)); 72 return prevunique; 73 } 74 75 76 IDBCursor::IDBCursor(PassRefPtr<IDBCursorBackendInterface> backend, IndexedDB::CursorDirection direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction) 77 : m_backend(backend) 78 , m_request(request) 79 , m_direction(direction) 80 , m_source(source) 81 , m_transaction(transaction) 82 , m_transactionNotifier(transaction, this) 83 , m_gotValue(false) 84 { 85 ASSERT(m_backend); 86 ASSERT(m_request); 87 ASSERT(m_source->type() == IDBAny::IDBObjectStoreType || m_source->type() == IDBAny::IDBIndexType); 88 ASSERT(m_transaction); 89 ScriptWrappable::init(this); 90 } 91 92 IDBCursor::~IDBCursor() 93 { 94 } 95 96 PassRefPtr<IDBRequest> IDBCursor::update(ScriptState* state, ScriptValue& value, ExceptionState& es) 97 { 98 IDB_TRACE("IDBCursor::update"); 99 100 if (!m_gotValue) { 101 es.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage); 102 return 0; 103 } 104 if (isKeyCursor()) { 105 es.throwDOMException(InvalidStateError, IDBDatabase::isKeyCursorErrorMessage); 106 return 0; 107 } 108 if (isDeleted()) { 109 es.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage); 110 return 0; 111 } 112 if (m_transaction->isFinished()) { 113 es.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage); 114 return 0; 115 } 116 if (!m_transaction->isActive()) { 117 es.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage); 118 return 0; 119 } 120 if (m_transaction->isReadOnly()) { 121 es.throwDOMException(ReadOnlyError); 122 return 0; 123 } 124 125 RefPtr<IDBObjectStore> objectStore = effectiveObjectStore(); 126 const IDBKeyPath& keyPath = objectStore->metadata().keyPath; 127 const bool usesInLineKeys = !keyPath.isNull(); 128 if (usesInLineKeys) { 129 RefPtr<IDBKey> keyPathKey = createIDBKeyFromScriptValueAndKeyPath(m_request->requestState(), value, keyPath); 130 if (!keyPathKey || !keyPathKey->isEqual(m_currentPrimaryKey.get())) { 131 es.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."); 132 return 0; 133 } 134 } 135 136 return objectStore->put(IDBDatabaseBackendInterface::CursorUpdate, IDBAny::create(this), state, value, m_currentPrimaryKey, es); 137 } 138 139 void IDBCursor::advance(unsigned long count, ExceptionState& es) 140 { 141 IDB_TRACE("IDBCursor::advance"); 142 if (!m_gotValue) { 143 es.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage); 144 return; 145 } 146 if (isDeleted()) { 147 es.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage); 148 return; 149 } 150 151 if (m_transaction->isFinished()) { 152 es.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage); 153 return; 154 } 155 if (!m_transaction->isActive()) { 156 es.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage); 157 return; 158 } 159 160 if (!count) { 161 es.throwTypeError(); 162 return; 163 } 164 165 m_request->setPendingCursor(this); 166 m_gotValue = false; 167 m_backend->advance(count, m_request); 168 } 169 170 void IDBCursor::continueFunction(ScriptExecutionContext* context, const ScriptValue& keyValue, ExceptionState& es) 171 { 172 DOMRequestState requestState(context); 173 RefPtr<IDBKey> key = keyValue.isUndefined() ? 0 : scriptValueToIDBKey(&requestState, keyValue); 174 continueFunction(key.release(), es); 175 } 176 177 void IDBCursor::continueFunction(PassRefPtr<IDBKey> key, ExceptionState& es) 178 { 179 IDB_TRACE("IDBCursor::continue"); 180 if (key && !key->isValid()) { 181 es.throwDOMException(DataError, IDBDatabase::notValidKeyErrorMessage); 182 return; 183 } 184 185 if (m_transaction->isFinished()) { 186 es.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage); 187 return; 188 } 189 if (!m_transaction->isActive()) { 190 es.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage); 191 return; 192 } 193 194 if (!m_gotValue) { 195 es.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage); 196 return; 197 } 198 199 if (isDeleted()) { 200 es.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage); 201 return; 202 } 203 204 if (key) { 205 ASSERT(m_currentKey); 206 if (m_direction == IndexedDB::CursorNext || m_direction == IndexedDB::CursorNextNoDuplicate) { 207 if (!m_currentKey->isLessThan(key.get())) { 208 es.throwDOMException(DataError, "The parameter is less than or equal to this cursor's position."); 209 return; 210 } 211 } else { 212 if (!key->isLessThan(m_currentKey.get())) { 213 es.throwDOMException(DataError, "The parameter is greater than or equal to this cursor's position."); 214 return; 215 } 216 } 217 } 218 219 // FIXME: We're not using the context from when continue was called, which means the callback 220 // will be on the original context openCursor was called on. Is this right? 221 m_request->setPendingCursor(this); 222 m_gotValue = false; 223 m_backend->continueFunction(key, m_request); 224 } 225 226 PassRefPtr<IDBRequest> IDBCursor::deleteFunction(ScriptExecutionContext* context, ExceptionState& es) 227 { 228 IDB_TRACE("IDBCursor::delete"); 229 if (m_transaction->isFinished()) { 230 es.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage); 231 return 0; 232 } 233 if (!m_transaction->isActive()) { 234 es.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage); 235 return 0; 236 } 237 if (m_transaction->isReadOnly()) { 238 es.throwDOMException(ReadOnlyError); 239 return 0; 240 } 241 242 if (!m_gotValue) { 243 es.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage); 244 return 0; 245 } 246 if (isKeyCursor()) { 247 es.throwDOMException(InvalidStateError, IDBDatabase::isKeyCursorErrorMessage); 248 return 0; 249 } 250 if (isDeleted()) { 251 es.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage); 252 return 0; 253 } 254 255 RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(m_currentPrimaryKey, es); 256 ASSERT(!es.hadException()); 257 258 RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); 259 m_transaction->backendDB()->deleteRange(m_transaction->id(), effectiveObjectStore()->id(), keyRange, request); 260 return request.release(); 261 } 262 263 void IDBCursor::postSuccessHandlerCallback() 264 { 265 if (m_backend) 266 m_backend->postSuccessHandlerCallback(); 267 } 268 269 void IDBCursor::close() 270 { 271 // The notifier may be the last reference to this cursor. 272 RefPtr<IDBCursor> protect(this); 273 m_transactionNotifier.cursorFinished(); 274 if (m_request) { 275 m_request->finishCursor(); 276 m_request.clear(); 277 } 278 m_backend.clear(); 279 } 280 281 void IDBCursor::setValueReady(DOMRequestState* state, PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, ScriptValue& value) 282 { 283 m_currentKey = key; 284 m_currentKeyValue = idbKeyToScriptValue(state, m_currentKey); 285 286 m_currentPrimaryKey = primaryKey; 287 m_currentPrimaryKeyValue = idbKeyToScriptValue(state, m_currentPrimaryKey); 288 289 if (isCursorWithValue()) { 290 RefPtr<IDBObjectStore> objectStore = effectiveObjectStore(); 291 const IDBObjectStoreMetadata metadata = objectStore->metadata(); 292 if (metadata.autoIncrement && !metadata.keyPath.isNull()) { 293 #ifndef NDEBUG 294 RefPtr<IDBKey> expectedKey = createIDBKeyFromScriptValueAndKeyPath(m_request->requestState(), value, metadata.keyPath); 295 ASSERT(!expectedKey || expectedKey->isEqual(m_currentPrimaryKey.get())); 296 #endif 297 bool injected = injectIDBKeyIntoScriptValue(m_request->requestState(), m_currentPrimaryKey, value, metadata.keyPath); 298 // FIXME: There is no way to report errors here. Move this into onSuccessWithContinuation so that we can abort the transaction there. See: https://bugs.webkit.org/show_bug.cgi?id=92278 299 ASSERT_UNUSED(injected, injected); 300 } 301 } 302 m_currentValue = value; 303 304 m_gotValue = true; 305 } 306 307 PassRefPtr<IDBObjectStore> IDBCursor::effectiveObjectStore() 308 { 309 if (m_source->type() == IDBAny::IDBObjectStoreType) 310 return m_source->idbObjectStore(); 311 RefPtr<IDBIndex> index = m_source->idbIndex(); 312 return index->objectStore(); 313 } 314 315 bool IDBCursor::isDeleted() const 316 { 317 if (m_source->type() == IDBAny::IDBObjectStoreType) 318 return m_source->idbObjectStore()->isDeleted(); 319 return m_source->idbIndex()->isDeleted(); 320 } 321 322 IndexedDB::CursorDirection IDBCursor::stringToDirection(const String& directionString, ExceptionState& es) 323 { 324 if (directionString.isNull() || directionString == IDBCursor::directionNext()) 325 return IndexedDB::CursorNext; 326 if (directionString == IDBCursor::directionNextUnique()) 327 return IndexedDB::CursorNextNoDuplicate; 328 if (directionString == IDBCursor::directionPrev()) 329 return IndexedDB::CursorPrev; 330 if (directionString == IDBCursor::directionPrevUnique()) 331 return IndexedDB::CursorPrevNoDuplicate; 332 333 es.throwTypeError(); 334 return IndexedDB::CursorNext; 335 } 336 337 const AtomicString& IDBCursor::directionToString(unsigned short direction) 338 { 339 switch (direction) { 340 case IndexedDB::CursorNext: 341 return IDBCursor::directionNext(); 342 343 case IndexedDB::CursorNextNoDuplicate: 344 return IDBCursor::directionNextUnique(); 345 346 case IndexedDB::CursorPrev: 347 return IDBCursor::directionPrev(); 348 349 case IndexedDB::CursorPrevNoDuplicate: 350 return IDBCursor::directionPrevUnique(); 351 352 default: 353 ASSERT_NOT_REACHED(); 354 return IDBCursor::directionNext(); 355 } 356 } 357 358 } // namespace WebCore 359