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 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 14 * its contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "config.h" 30 #include "modules/indexeddb/IDBRequest.h" 31 32 #include "bindings/v8/ExceptionState.h" 33 #include "bindings/v8/ExceptionStatePlaceholder.h" 34 #include "bindings/v8/IDBBindingUtilities.h" 35 #include "core/dom/DOMError.h" 36 #include "core/dom/EventListener.h" 37 #include "core/dom/EventNames.h" 38 #include "core/dom/EventQueue.h" 39 #include "core/dom/ScriptExecutionContext.h" 40 #include "modules/indexeddb/IDBCursorBackendInterface.h" 41 #include "modules/indexeddb/IDBCursorWithValue.h" 42 #include "modules/indexeddb/IDBDatabase.h" 43 #include "modules/indexeddb/IDBEventDispatcher.h" 44 #include "modules/indexeddb/IDBTracing.h" 45 #include "modules/indexeddb/IDBTransaction.h" 46 47 namespace WebCore { 48 49 PassRefPtr<IDBRequest> IDBRequest::create(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, IDBTransaction* transaction) 50 { 51 RefPtr<IDBRequest> request(adoptRef(new IDBRequest(context, source, IDBDatabaseBackendInterface::NormalTask, transaction))); 52 request->suspendIfNeeded(); 53 // Requests associated with IDBFactory (open/deleteDatabase/getDatabaseNames) are not associated with transactions. 54 if (transaction) 55 transaction->registerRequest(request.get()); 56 return request.release(); 57 } 58 59 PassRefPtr<IDBRequest> IDBRequest::create(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, IDBDatabaseBackendInterface::TaskType taskType, IDBTransaction* transaction) 60 { 61 RefPtr<IDBRequest> request(adoptRef(new IDBRequest(context, source, taskType, transaction))); 62 request->suspendIfNeeded(); 63 // Requests associated with IDBFactory (open/deleteDatabase/getDatabaseNames) are not associated with transactions. 64 if (transaction) 65 transaction->registerRequest(request.get()); 66 return request.release(); 67 } 68 69 IDBRequest::IDBRequest(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, IDBDatabaseBackendInterface::TaskType taskType, IDBTransaction* transaction) 70 : ActiveDOMObject(context) 71 , m_result(0) 72 , m_contextStopped(false) 73 , m_transaction(transaction) 74 , m_readyState(PENDING) 75 , m_requestAborted(false) 76 , m_source(source) 77 , m_taskType(taskType) 78 , m_hasPendingActivity(true) 79 , m_cursorType(IndexedDB::CursorKeyAndValue) 80 , m_cursorDirection(IndexedDB::CursorNext) 81 , m_cursorFinished(false) 82 , m_pendingCursor(0) 83 , m_didFireUpgradeNeededEvent(false) 84 , m_preventPropagation(false) 85 , m_requestState(context) 86 { 87 ScriptWrappable::init(this); 88 } 89 90 IDBRequest::~IDBRequest() 91 { 92 ASSERT(m_readyState == DONE || m_readyState == EarlyDeath || !scriptExecutionContext()); 93 } 94 95 PassRefPtr<IDBAny> IDBRequest::result(ExceptionState& es) const 96 { 97 if (m_readyState != DONE) { 98 es.throwDOMException(InvalidStateError, IDBDatabase::requestNotFinishedErrorMessage); 99 return 0; 100 } 101 return m_result; 102 } 103 104 PassRefPtr<DOMError> IDBRequest::error(ExceptionState& es) const 105 { 106 if (m_readyState != DONE) { 107 es.throwDOMException(InvalidStateError, IDBDatabase::requestNotFinishedErrorMessage); 108 return 0; 109 } 110 return m_error; 111 } 112 113 PassRefPtr<IDBAny> IDBRequest::source() const 114 { 115 return m_source; 116 } 117 118 PassRefPtr<IDBTransaction> IDBRequest::transaction() const 119 { 120 return m_transaction; 121 } 122 123 const String& IDBRequest::readyState() const 124 { 125 ASSERT(m_readyState == PENDING || m_readyState == DONE); 126 DEFINE_STATIC_LOCAL(AtomicString, pending, ("pending", AtomicString::ConstructFromLiteral)); 127 DEFINE_STATIC_LOCAL(AtomicString, done, ("done", AtomicString::ConstructFromLiteral)); 128 129 if (m_readyState == PENDING) 130 return pending; 131 132 return done; 133 } 134 135 void IDBRequest::markEarlyDeath() 136 { 137 ASSERT(m_readyState == PENDING); 138 m_readyState = EarlyDeath; 139 if (m_transaction) { 140 m_transaction->unregisterRequest(this); 141 m_transaction.clear(); 142 } 143 } 144 145 void IDBRequest::abort() 146 { 147 ASSERT(!m_requestAborted); 148 if (m_contextStopped || !scriptExecutionContext()) 149 return; 150 ASSERT(m_readyState == PENDING || m_readyState == DONE); 151 if (m_readyState == DONE) 152 return; 153 154 // Enqueued events may be the only reference to this object. 155 RefPtr<IDBRequest> self(this); 156 157 EventQueue* eventQueue = scriptExecutionContext()->eventQueue(); 158 for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) { 159 bool removed = eventQueue->cancelEvent(m_enqueuedEvents[i].get()); 160 ASSERT_UNUSED(removed, removed); 161 } 162 m_enqueuedEvents.clear(); 163 164 m_error.clear(); 165 m_result.clear(); 166 onError(DOMError::create(AbortError, "The transaction was aborted, so the request cannot be fulfilled.")); 167 m_requestAborted = true; 168 } 169 170 void IDBRequest::setCursorDetails(IndexedDB::CursorType cursorType, IndexedDB::CursorDirection direction) 171 { 172 ASSERT(m_readyState == PENDING); 173 ASSERT(!m_pendingCursor); 174 m_cursorType = cursorType; 175 m_cursorDirection = direction; 176 } 177 178 void IDBRequest::setPendingCursor(PassRefPtr<IDBCursor> cursor) 179 { 180 ASSERT(m_readyState == DONE); 181 ASSERT(scriptExecutionContext()); 182 ASSERT(m_transaction); 183 ASSERT(!m_pendingCursor); 184 ASSERT(cursor == getResultCursor()); 185 186 m_pendingCursor = cursor; 187 m_result.clear(); 188 m_readyState = PENDING; 189 m_error.clear(); 190 m_transaction->registerRequest(this); 191 } 192 193 PassRefPtr<IDBCursor> IDBRequest::getResultCursor() 194 { 195 if (!m_result) 196 return 0; 197 if (m_result->type() == IDBAny::IDBCursorType) 198 return m_result->idbCursor(); 199 if (m_result->type() == IDBAny::IDBCursorWithValueType) 200 return m_result->idbCursorWithValue(); 201 return 0; 202 } 203 204 void IDBRequest::setResultCursor(PassRefPtr<IDBCursor> cursor, PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, const ScriptValue& value) 205 { 206 ASSERT(m_readyState == PENDING); 207 m_cursorKey = key; 208 m_cursorPrimaryKey = primaryKey; 209 m_cursorValue = value; 210 m_result = IDBAny::create(cursor); 211 } 212 213 void IDBRequest::finishCursor() 214 { 215 m_cursorFinished = true; 216 if (m_readyState != PENDING) 217 m_hasPendingActivity = false; 218 } 219 220 bool IDBRequest::shouldEnqueueEvent() const 221 { 222 if (m_contextStopped || !scriptExecutionContext()) 223 return false; 224 ASSERT(m_readyState == PENDING || m_readyState == DONE); 225 if (m_requestAborted) 226 return false; 227 ASSERT(m_readyState == PENDING); 228 ASSERT(!m_error && !m_result); 229 return true; 230 } 231 232 void IDBRequest::onError(PassRefPtr<DOMError> error) 233 { 234 IDB_TRACE("IDBRequest::onError()"); 235 if (!shouldEnqueueEvent()) 236 return; 237 238 m_error = error; 239 m_pendingCursor.clear(); 240 enqueueEvent(Event::create(eventNames().errorEvent, true, true)); 241 } 242 243 static PassRefPtr<Event> createSuccessEvent() 244 { 245 return Event::create(eventNames().successEvent, false, false); 246 } 247 248 void IDBRequest::onSuccess(const Vector<String>& stringList) 249 { 250 IDB_TRACE("IDBRequest::onSuccess(StringList)"); 251 if (!shouldEnqueueEvent()) 252 return; 253 254 RefPtr<DOMStringList> domStringList = DOMStringList::create(); 255 for (size_t i = 0; i < stringList.size(); ++i) 256 domStringList->append(stringList[i]); 257 m_result = IDBAny::create(domStringList.release()); 258 enqueueEvent(createSuccessEvent()); 259 } 260 261 void IDBRequest::onSuccess(PassRefPtr<IDBCursorBackendInterface> backend, PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> buffer) 262 { 263 IDB_TRACE("IDBRequest::onSuccess(IDBCursor)"); 264 if (!shouldEnqueueEvent()) 265 return; 266 267 DOMRequestState::Scope scope(m_requestState); 268 ScriptValue value = deserializeIDBValueBuffer(requestState(), buffer); 269 ASSERT(!m_pendingCursor); 270 RefPtr<IDBCursor> cursor; 271 switch (m_cursorType) { 272 case IndexedDB::CursorKeyOnly: 273 cursor = IDBCursor::create(backend, m_cursorDirection, this, m_source.get(), m_transaction.get()); 274 break; 275 case IndexedDB::CursorKeyAndValue: 276 cursor = IDBCursorWithValue::create(backend, m_cursorDirection, this, m_source.get(), m_transaction.get()); 277 break; 278 default: 279 ASSERT_NOT_REACHED(); 280 } 281 setResultCursor(cursor, key, primaryKey, value); 282 283 enqueueEvent(createSuccessEvent()); 284 } 285 286 void IDBRequest::onSuccess(PassRefPtr<IDBKey> idbKey) 287 { 288 IDB_TRACE("IDBRequest::onSuccess(IDBKey)"); 289 if (!shouldEnqueueEvent()) 290 return; 291 292 if (idbKey && idbKey->isValid()) { 293 DOMRequestState::Scope scope(m_requestState); 294 m_result = IDBAny::create(idbKeyToScriptValue(requestState(), idbKey)); 295 } else 296 m_result = IDBAny::createInvalid(); 297 enqueueEvent(createSuccessEvent()); 298 } 299 300 void IDBRequest::onSuccess(PassRefPtr<SharedBuffer> valueBuffer) 301 { 302 IDB_TRACE("IDBRequest::onSuccess(SharedBuffer)"); 303 if (!shouldEnqueueEvent()) 304 return; 305 306 if (m_pendingCursor) { 307 m_pendingCursor->close(); 308 m_pendingCursor.clear(); 309 } 310 311 DOMRequestState::Scope scope(m_requestState); 312 ScriptValue value = deserializeIDBValueBuffer(requestState(), valueBuffer); 313 onSuccessInternal(value); 314 } 315 316 #ifndef NDEBUG 317 static PassRefPtr<IDBObjectStore> effectiveObjectStore(PassRefPtr<IDBAny> source) 318 { 319 if (source->type() == IDBAny::IDBObjectStoreType) 320 return source->idbObjectStore(); 321 if (source->type() == IDBAny::IDBIndexType) 322 return source->idbIndex()->objectStore(); 323 324 ASSERT_NOT_REACHED(); 325 return 0; 326 } 327 #endif 328 329 void IDBRequest::onSuccess(PassRefPtr<SharedBuffer> valueBuffer, PassRefPtr<IDBKey> prpPrimaryKey, const IDBKeyPath& keyPath) 330 { 331 IDB_TRACE("IDBRequest::onSuccess(SharedBuffer, IDBKey, IDBKeyPath)"); 332 if (!shouldEnqueueEvent()) 333 return; 334 335 #ifndef NDEBUG 336 ASSERT(keyPath == effectiveObjectStore(m_source)->keyPath()); 337 #endif 338 DOMRequestState::Scope scope(m_requestState); 339 ScriptValue value = deserializeIDBValueBuffer(requestState(), valueBuffer); 340 341 RefPtr<IDBKey> primaryKey = prpPrimaryKey; 342 #ifndef NDEBUG 343 RefPtr<IDBKey> expectedKey = createIDBKeyFromScriptValueAndKeyPath(requestState(), value, keyPath); 344 ASSERT(!expectedKey || expectedKey->isEqual(primaryKey.get())); 345 #endif 346 bool injected = injectIDBKeyIntoScriptValue(requestState(), primaryKey, value, keyPath); 347 ASSERT_UNUSED(injected, injected); 348 onSuccessInternal(value); 349 } 350 351 void IDBRequest::onSuccess(int64_t value) 352 { 353 IDB_TRACE("IDBRequest::onSuccess(int64_t)"); 354 if (!shouldEnqueueEvent()) 355 return; 356 return onSuccessInternal(SerializedScriptValue::numberValue(value)); 357 } 358 359 void IDBRequest::onSuccess() 360 { 361 IDB_TRACE("IDBRequest::onSuccess()"); 362 if (!shouldEnqueueEvent()) 363 return; 364 return onSuccessInternal(SerializedScriptValue::undefinedValue()); 365 } 366 367 void IDBRequest::onSuccessInternal(PassRefPtr<SerializedScriptValue> value) 368 { 369 ASSERT(!m_contextStopped); 370 DOMRequestState::Scope scope(m_requestState); 371 return onSuccessInternal(deserializeIDBValue(requestState(), value)); 372 } 373 374 void IDBRequest::onSuccessInternal(const ScriptValue& value) 375 { 376 m_result = IDBAny::create(value); 377 ASSERT(!m_pendingCursor); 378 enqueueEvent(createSuccessEvent()); 379 } 380 381 void IDBRequest::onSuccess(PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> buffer) 382 { 383 IDB_TRACE("IDBRequest::onSuccess(key, primaryKey, value)"); 384 if (!shouldEnqueueEvent()) 385 return; 386 387 DOMRequestState::Scope scope(m_requestState); 388 ScriptValue value = deserializeIDBValueBuffer(requestState(), buffer); 389 ASSERT(m_pendingCursor); 390 setResultCursor(m_pendingCursor.release(), key, primaryKey, value); 391 enqueueEvent(createSuccessEvent()); 392 } 393 394 bool IDBRequest::hasPendingActivity() const 395 { 396 // FIXME: In an ideal world, we should return true as long as anyone has a or can 397 // get a handle to us and we have event listeners. This is order to handle 398 // user generated events properly. 399 return m_hasPendingActivity && !m_contextStopped; 400 } 401 402 void IDBRequest::stop() 403 { 404 if (m_contextStopped) 405 return; 406 407 m_contextStopped = true; 408 m_requestState.clear(); 409 if (m_readyState == PENDING) 410 markEarlyDeath(); 411 } 412 413 const AtomicString& IDBRequest::interfaceName() const 414 { 415 return eventNames().interfaceForIDBRequest; 416 } 417 418 ScriptExecutionContext* IDBRequest::scriptExecutionContext() const 419 { 420 return ActiveDOMObject::scriptExecutionContext(); 421 } 422 423 bool IDBRequest::dispatchEvent(PassRefPtr<Event> event) 424 { 425 IDB_TRACE("IDBRequest::dispatchEvent"); 426 ASSERT(m_readyState == PENDING); 427 ASSERT(!m_contextStopped); 428 ASSERT(m_hasPendingActivity); 429 ASSERT(m_enqueuedEvents.size()); 430 ASSERT(scriptExecutionContext()); 431 ASSERT(event->target() == this); 432 ASSERT_WITH_MESSAGE(m_readyState < DONE, "When dispatching event %s, m_readyState < DONE(%d), was %d", event->type().string().utf8().data(), DONE, m_readyState); 433 434 DOMRequestState::Scope scope(m_requestState); 435 436 if (event->type() != eventNames().blockedEvent) 437 m_readyState = DONE; 438 439 for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) { 440 if (m_enqueuedEvents[i].get() == event.get()) 441 m_enqueuedEvents.remove(i); 442 } 443 444 Vector<RefPtr<EventTarget> > targets; 445 targets.append(this); 446 if (m_transaction && !m_preventPropagation) { 447 targets.append(m_transaction); 448 // If there ever are events that are associated with a database but 449 // that do not have a transaction, then this will not work and we need 450 // this object to actually hold a reference to the database (to ensure 451 // it stays alive). 452 targets.append(m_transaction->db()); 453 } 454 455 // Cursor properties should not updated until the success event is being dispatched. 456 RefPtr<IDBCursor> cursorToNotify; 457 if (event->type() == eventNames().successEvent) { 458 cursorToNotify = getResultCursor(); 459 if (cursorToNotify) { 460 cursorToNotify->setValueReady(requestState(), m_cursorKey.release(), m_cursorPrimaryKey.release(), m_cursorValue); 461 m_cursorValue.clear(); 462 } 463 } 464 465 if (event->type() == eventNames().upgradeneededEvent) { 466 ASSERT(!m_didFireUpgradeNeededEvent); 467 m_didFireUpgradeNeededEvent = true; 468 } 469 470 // FIXME: When we allow custom event dispatching, this will probably need to change. 471 ASSERT_WITH_MESSAGE(event->type() == eventNames().successEvent || event->type() == eventNames().errorEvent || event->type() == eventNames().blockedEvent || event->type() == eventNames().upgradeneededEvent, "event type was %s", event->type().string().utf8().data()); 472 const bool setTransactionActive = m_transaction && (event->type() == eventNames().successEvent || event->type() == eventNames().upgradeneededEvent || (event->type() == eventNames().errorEvent && !m_requestAborted)); 473 474 if (setTransactionActive) 475 m_transaction->setActive(true); 476 477 bool dontPreventDefault = IDBEventDispatcher::dispatch(event.get(), targets); 478 479 if (m_transaction) { 480 if (m_readyState == DONE) 481 m_transaction->unregisterRequest(this); 482 483 // Possibly abort the transaction. This must occur after unregistering (so this request 484 // doesn't receive a second error) and before deactivating (which might trigger commit). 485 if (event->type() == eventNames().errorEvent && dontPreventDefault && !m_requestAborted) { 486 m_transaction->setError(m_error); 487 m_transaction->abort(IGNORE_EXCEPTION); 488 } 489 490 // If this was the last request in the transaction's list, it may commit here. 491 if (setTransactionActive) 492 m_transaction->setActive(false); 493 } 494 495 if (cursorToNotify) 496 cursorToNotify->postSuccessHandlerCallback(); 497 498 if (m_readyState == DONE && (!cursorToNotify || m_cursorFinished) && event->type() != eventNames().upgradeneededEvent) 499 m_hasPendingActivity = false; 500 501 return dontPreventDefault; 502 } 503 504 void IDBRequest::uncaughtExceptionInEventHandler() 505 { 506 if (m_transaction && !m_requestAborted) { 507 m_transaction->setError(DOMError::create(AbortError, "Uncaught exception in event handler.")); 508 m_transaction->abort(IGNORE_EXCEPTION); 509 } 510 } 511 512 void IDBRequest::transactionDidFinishAndDispatch() 513 { 514 ASSERT(m_transaction); 515 ASSERT(m_transaction->isVersionChange()); 516 ASSERT(m_readyState == DONE); 517 ASSERT(scriptExecutionContext()); 518 m_transaction.clear(); 519 m_readyState = PENDING; 520 } 521 522 void IDBRequest::enqueueEvent(PassRefPtr<Event> event) 523 { 524 ASSERT(m_readyState == PENDING || m_readyState == DONE); 525 526 if (m_contextStopped || !scriptExecutionContext()) 527 return; 528 529 ASSERT_WITH_MESSAGE(m_readyState == PENDING || m_didFireUpgradeNeededEvent, "When queueing event %s, m_readyState was %d", event->type().string().utf8().data(), m_readyState); 530 531 EventQueue* eventQueue = scriptExecutionContext()->eventQueue(); 532 event->setTarget(this); 533 534 if (eventQueue->enqueueEvent(event.get())) 535 m_enqueuedEvents.append(event); 536 } 537 538 EventTargetData* IDBRequest::eventTargetData() 539 { 540 return &m_eventTargetData; 541 } 542 543 EventTargetData* IDBRequest::ensureEventTargetData() 544 { 545 return &m_eventTargetData; 546 } 547 548 } // namespace WebCore 549