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 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 56 const AtomicString& IDBCursor::directionNext() 57 { 58 DEFINE_STATIC_LOCAL(AtomicString, next, ("next", AtomicString::ConstructFromLiteral)); 59 return next; 60 } 61 62 const AtomicString& IDBCursor::directionNextUnique() 63 { 64 DEFINE_STATIC_LOCAL(AtomicString, nextunique, ("nextunique", AtomicString::ConstructFromLiteral)); 65 return nextunique; 66 } 67 68 const AtomicString& IDBCursor::directionPrev() 69 { 70 DEFINE_STATIC_LOCAL(AtomicString, prev, ("prev", AtomicString::ConstructFromLiteral)); 71 return prev; 72 } 73 74 const AtomicString& IDBCursor::directionPrevUnique() 75 { 76 DEFINE_STATIC_LOCAL(AtomicString, prevunique, ("prevunique", AtomicString::ConstructFromLiteral)); 77 return prevunique; 78 } 79 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 98 IDBCursor::~IDBCursor() 99 { 100 handleBlobAcks(); 101 } 102 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 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 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 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 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 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 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 299 void IDBCursor::postSuccessHandlerCallback() 300 { 301 if (m_backend) 302 m_backend->postSuccessHandlerCallback(); 303 } 304 305 void IDBCursor::close() 306 { 307 handleBlobAcks(); 308 m_request.clear(); 309 m_backend.clear(); 310 } 311 312 ScriptValue IDBCursor::key(ScriptState* scriptState) 313 { 314 m_keyDirty = false; 315 return idbKeyToScriptValue(scriptState, m_key); 316 } 317 318 ScriptValue IDBCursor::primaryKey(ScriptState* scriptState) 319 { 320 m_primaryKeyDirty = false; 321 return idbKeyToScriptValue(scriptState, m_primaryKey); 322 } 323 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 346 ScriptValue IDBCursor::source(ScriptState* scriptState) const 347 { 348 return idbAnyToScriptValue(scriptState, m_source); 349 } 350 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 369 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 376 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 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 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 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