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 "IDBCursor.h" 28 29#if ENABLE(INDEXED_DATABASE) 30 31#include "IDBAny.h" 32#include "IDBBindingUtilities.h" 33#include "IDBCallbacks.h" 34#include "IDBCursorBackend.h" 35#include "IDBKey.h" 36#include "IDBObjectStore.h" 37#include "IDBRequest.h" 38#include "IDBTransaction.h" 39#include "Logging.h" 40#include "ScriptExecutionContext.h" 41#include <inspector/ScriptCallStack.h> 42#include <limits> 43 44namespace WebCore { 45 46PassRefPtr<IDBCursor> IDBCursor::create(PassRefPtr<IDBCursorBackend> backend, IndexedDB::CursorDirection direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction) 47{ 48 return adoptRef(new IDBCursor(backend, direction, request, source, transaction)); 49} 50 51const AtomicString& IDBCursor::directionNext() 52{ 53 DEPRECATED_DEFINE_STATIC_LOCAL(AtomicString, next, ("next", AtomicString::ConstructFromLiteral)); 54 return next; 55} 56 57const AtomicString& IDBCursor::directionNextUnique() 58{ 59 DEPRECATED_DEFINE_STATIC_LOCAL(AtomicString, nextunique, ("nextunique", AtomicString::ConstructFromLiteral)); 60 return nextunique; 61} 62 63const AtomicString& IDBCursor::directionPrev() 64{ 65 DEPRECATED_DEFINE_STATIC_LOCAL(AtomicString, prev, ("prev", AtomicString::ConstructFromLiteral)); 66 return prev; 67} 68 69const AtomicString& IDBCursor::directionPrevUnique() 70{ 71 DEPRECATED_DEFINE_STATIC_LOCAL(AtomicString, prevunique, ("prevunique", AtomicString::ConstructFromLiteral)); 72 return prevunique; 73} 74 75 76IDBCursor::IDBCursor(PassRefPtr<IDBCursorBackend> 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} 90 91IDBCursor::~IDBCursor() 92{ 93} 94 95const String& IDBCursor::direction() const 96{ 97 LOG(StorageAPI, "IDBCursor::direction"); 98 return directionToString(m_direction); 99} 100 101const Deprecated::ScriptValue& IDBCursor::key() const 102{ 103 LOG(StorageAPI, "IDBCursor::key"); 104 return m_currentKeyValue; 105} 106 107const Deprecated::ScriptValue& IDBCursor::primaryKey() const 108{ 109 LOG(StorageAPI, "IDBCursor::primaryKey"); 110 return m_currentPrimaryKeyValue; 111} 112 113const Deprecated::ScriptValue& IDBCursor::value() const 114{ 115 LOG(StorageAPI, "IDBCursor::value"); 116 return m_currentValue; 117} 118 119IDBAny* IDBCursor::source() const 120{ 121 return m_source.get(); 122} 123 124PassRefPtr<IDBRequest> IDBCursor::update(JSC::ExecState* state, Deprecated::ScriptValue& value, ExceptionCode& ec) 125{ 126 LOG(StorageAPI, "IDBCursor::update"); 127 128 if (!m_gotValue || isKeyCursor()) { 129 ec = IDBDatabaseException::InvalidStateError; 130 return 0; 131 } 132 if (!m_transaction->isActive()) { 133 ec = IDBDatabaseException::TransactionInactiveError; 134 return 0; 135 } 136 if (m_transaction->isReadOnly()) { 137 ec = IDBDatabaseException::ReadOnlyError; 138 return 0; 139 } 140 141 RefPtr<IDBObjectStore> objectStore = effectiveObjectStore(); 142 const IDBKeyPath& keyPath = objectStore->metadata().keyPath; 143 const bool usesInLineKeys = !keyPath.isNull(); 144 if (usesInLineKeys) { 145 RefPtr<IDBKey> keyPathKey = createIDBKeyFromScriptValueAndKeyPath(m_request->requestState()->exec(), value, keyPath); 146 if (!keyPathKey || !keyPathKey->isEqual(m_currentPrimaryKey.get())) { 147 ec = IDBDatabaseException::DataError; 148 return 0; 149 } 150 } 151 152 return objectStore->put(IDBDatabaseBackend::CursorUpdate, IDBAny::create(this), state, value, m_currentPrimaryKey, ec); 153} 154 155void IDBCursor::advance(unsigned long count, ExceptionCode& ec) 156{ 157 ec = 0; 158 LOG(StorageAPI, "IDBCursor::advance"); 159 if (!m_gotValue) { 160 ec = IDBDatabaseException::InvalidStateError; 161 return; 162 } 163 164 if (!m_transaction->isActive()) { 165 ec = IDBDatabaseException::TransactionInactiveError; 166 return; 167 } 168 169 if (!count) { 170 ec = TypeError; 171 return; 172 } 173 174 m_request->setPendingCursor(this); 175 m_gotValue = false; 176 m_backend->advance(count, m_request, ec); 177 ASSERT(!ec); 178} 179 180void IDBCursor::continueFunction(ScriptExecutionContext* context, const Deprecated::ScriptValue& keyValue, ExceptionCode& ec) 181{ 182 DOMRequestState requestState(context); 183 RefPtr<IDBKey> key = scriptValueToIDBKey(&requestState, keyValue); 184 continueFunction(key.release(), ec); 185} 186 187void IDBCursor::continueFunction(PassRefPtr<IDBKey> key, ExceptionCode& ec) 188{ 189 ec = 0; 190 LOG(StorageAPI, "IDBCursor::continue"); 191 if (key && !key->isValid()) { 192 ec = IDBDatabaseException::DataError; 193 return; 194 } 195 196 if (!m_transaction->isActive()) { 197 ec = IDBDatabaseException::TransactionInactiveError; 198 return; 199 } 200 201 if (!m_gotValue) { 202 ec = IDBDatabaseException::InvalidStateError; 203 return; 204 } 205 206 if (key) { 207 ASSERT(m_currentKey); 208 if (m_direction == IndexedDB::CursorDirection::Next || m_direction == IndexedDB::CursorDirection::NextNoDuplicate) { 209 if (!m_currentKey->isLessThan(key.get())) { 210 ec = IDBDatabaseException::DataError; 211 return; 212 } 213 } else { 214 if (!key->isLessThan(m_currentKey.get())) { 215 ec = IDBDatabaseException::DataError; 216 return; 217 } 218 } 219 } 220 221 // FIXME: We're not using the context from when continue was called, which means the callback 222 // will be on the original context openCursor was called on. Is this right? 223 m_request->setPendingCursor(this); 224 m_gotValue = false; 225 m_backend->continueFunction(key, m_request, ec); 226 ASSERT(!ec); 227} 228 229PassRefPtr<IDBRequest> IDBCursor::deleteFunction(ScriptExecutionContext* context, ExceptionCode& ec) 230{ 231 ec = 0; 232 LOG(StorageAPI, "IDBCursor::delete"); 233 if (!m_transaction->isActive()) { 234 ec = IDBDatabaseException::TransactionInactiveError; 235 return 0; 236 } 237 if (m_transaction->isReadOnly()) { 238 ec = IDBDatabaseException::ReadOnlyError; 239 return 0; 240 } 241 242 if (!m_gotValue || isKeyCursor()) { 243 ec = IDBDatabaseException::InvalidStateError; 244 return 0; 245 } 246 RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); 247 m_backend->deleteFunction(request, ec); 248 ASSERT(!ec); 249 return request.release(); 250} 251 252void IDBCursor::postSuccessHandlerCallback() 253{ 254 m_backend->postSuccessHandlerCallback(); 255} 256 257void IDBCursor::close() 258{ 259 m_transactionNotifier.cursorFinished(); 260 if (m_request) { 261 m_request->finishCursor(); 262 m_request.clear(); 263 } 264} 265 266void IDBCursor::setValueReady(DOMRequestState* state, PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, Deprecated::ScriptValue& value) 267{ 268 m_currentKey = key; 269 m_currentKeyValue = idbKeyToScriptValue(state, m_currentKey); 270 271 m_currentPrimaryKey = primaryKey; 272 m_currentPrimaryKeyValue = idbKeyToScriptValue(state, m_currentPrimaryKey); 273 274 if (!isKeyCursor()) { 275 RefPtr<IDBObjectStore> objectStore = effectiveObjectStore(); 276 const IDBObjectStoreMetadata metadata = objectStore->metadata(); 277 if (metadata.autoIncrement && !metadata.keyPath.isNull()) { 278#ifndef NDEBUG 279 RefPtr<IDBKey> expectedKey = createIDBKeyFromScriptValueAndKeyPath(m_request->requestState()->exec(), value, metadata.keyPath); 280 ASSERT(!expectedKey || expectedKey->isEqual(m_currentPrimaryKey.get())); 281#endif 282 bool injected = injectIDBKeyIntoScriptValue(m_request->requestState(), m_currentPrimaryKey, value, metadata.keyPath); 283 // 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 284 ASSERT_UNUSED(injected, injected); 285 } 286 } 287 m_currentValue = value; 288 289 m_gotValue = true; 290} 291 292PassRefPtr<IDBObjectStore> IDBCursor::effectiveObjectStore() 293{ 294 if (m_source->type() == IDBAny::IDBObjectStoreType) 295 return m_source->idbObjectStore(); 296 RefPtr<IDBIndex> index = m_source->idbIndex(); 297 return index->objectStore(); 298} 299 300IndexedDB::CursorDirection IDBCursor::stringToDirection(const String& directionString, ExceptionCode& ec) 301{ 302 if (directionString == IDBCursor::directionNext()) 303 return IndexedDB::CursorDirection::Next; 304 if (directionString == IDBCursor::directionNextUnique()) 305 return IndexedDB::CursorDirection::NextNoDuplicate; 306 if (directionString == IDBCursor::directionPrev()) 307 return IndexedDB::CursorDirection::Prev; 308 if (directionString == IDBCursor::directionPrevUnique()) 309 return IndexedDB::CursorDirection::PrevNoDuplicate; 310 311 ec = TypeError; 312 return IndexedDB::CursorDirection::Next; 313} 314 315const AtomicString& IDBCursor::directionToString(IndexedDB::CursorDirection direction) 316{ 317 switch (direction) { 318 case IndexedDB::CursorDirection::Next: 319 return IDBCursor::directionNext(); 320 321 case IndexedDB::CursorDirection::NextNoDuplicate: 322 return IDBCursor::directionNextUnique(); 323 324 case IndexedDB::CursorDirection::Prev: 325 return IDBCursor::directionPrev(); 326 327 case IndexedDB::CursorDirection::PrevNoDuplicate: 328 return IDBCursor::directionPrevUnique(); 329 330 default: 331 ASSERT_NOT_REACHED(); 332 return IDBCursor::directionNext(); 333 } 334} 335 336} // namespace WebCore 337 338#endif // ENABLE(INDEXED_DATABASE) 339