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/ExecutionContext.h" 36 #include "core/events/EventQueue.h" 37 #include "modules/indexeddb/IDBCursorWithValue.h" 38 #include "modules/indexeddb/IDBDatabase.h" 39 #include "modules/indexeddb/IDBEventDispatcher.h" 40 #include "modules/indexeddb/IDBTracing.h" 41 #include "platform/SharedBuffer.h" 42 43 namespace WebCore { 44 45 PassRefPtr<IDBRequest> IDBRequest::create(ExecutionContext* context, PassRefPtr<IDBAny> source, IDBTransaction* transaction) 46 { 47 RefPtr<IDBRequest> request(adoptRef(new IDBRequest(context, source, transaction))); 48 request->suspendIfNeeded(); 49 // Requests associated with IDBFactory (open/deleteDatabase/getDatabaseNames) are not associated with transactions. 50 if (transaction) 51 transaction->registerRequest(request.get()); 52 return request.release(); 53 } 54 55 IDBRequest::IDBRequest(ExecutionContext* context, PassRefPtr<IDBAny> source, IDBTransaction* transaction) 56 : ActiveDOMObject(context) 57 , m_contextStopped(false) 58 , m_transaction(transaction) 59 , m_readyState(PENDING) 60 , m_requestAborted(false) 61 , m_source(source) 62 , m_hasPendingActivity(true) 63 , m_cursorType(IndexedDB::CursorKeyAndValue) 64 , m_cursorDirection(IndexedDB::CursorNext) 65 , m_pendingCursor(0) 66 , m_didFireUpgradeNeededEvent(false) 67 , m_preventPropagation(false) 68 , m_resultDirty(true) 69 , m_requestState(context) 70 { 71 ScriptWrappable::init(this); 72 } 73 74 IDBRequest::~IDBRequest() 75 { 76 ASSERT(m_readyState == DONE || m_readyState == EarlyDeath || !executionContext()); 77 } 78 79 ScriptValue IDBRequest::result(ExceptionState& exceptionState) 80 { 81 if (m_readyState != DONE) { 82 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::requestNotFinishedErrorMessage); 83 return ScriptValue(); 84 } 85 m_resultDirty = false; 86 return idbAnyToScriptValue(&m_requestState, m_result); 87 } 88 89 PassRefPtr<DOMError> IDBRequest::error(ExceptionState& exceptionState) const 90 { 91 if (m_readyState != DONE) { 92 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::requestNotFinishedErrorMessage); 93 return 0; 94 } 95 return m_error; 96 } 97 98 ScriptValue IDBRequest::source(ExecutionContext* context) const 99 { 100 DOMRequestState requestState(context); 101 return idbAnyToScriptValue(&requestState, m_source); 102 } 103 104 const String& IDBRequest::readyState() const 105 { 106 ASSERT(m_readyState == PENDING || m_readyState == DONE); 107 DEFINE_STATIC_LOCAL(AtomicString, pending, ("pending", AtomicString::ConstructFromLiteral)); 108 DEFINE_STATIC_LOCAL(AtomicString, done, ("done", AtomicString::ConstructFromLiteral)); 109 110 if (m_readyState == PENDING) 111 return pending; 112 113 return done; 114 } 115 116 void IDBRequest::abort() 117 { 118 ASSERT(!m_requestAborted); 119 if (m_contextStopped || !executionContext()) 120 return; 121 ASSERT(m_readyState == PENDING || m_readyState == DONE); 122 if (m_readyState == DONE) 123 return; 124 125 // Enqueued events may be the only reference to this object. 126 RefPtr<IDBRequest> self(this); 127 128 EventQueue* eventQueue = executionContext()->eventQueue(); 129 for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) { 130 bool removed = eventQueue->cancelEvent(m_enqueuedEvents[i].get()); 131 ASSERT_UNUSED(removed, removed); 132 } 133 m_enqueuedEvents.clear(); 134 135 m_error.clear(); 136 m_result.clear(); 137 onError(DOMError::create(AbortError, "The transaction was aborted, so the request cannot be fulfilled.")); 138 m_requestAborted = true; 139 } 140 141 void IDBRequest::setCursorDetails(IndexedDB::CursorType cursorType, IndexedDB::CursorDirection direction) 142 { 143 ASSERT(m_readyState == PENDING); 144 ASSERT(!m_pendingCursor); 145 m_cursorType = cursorType; 146 m_cursorDirection = direction; 147 } 148 149 void IDBRequest::setPendingCursor(PassRefPtr<IDBCursor> cursor) 150 { 151 ASSERT(m_readyState == DONE); 152 ASSERT(executionContext()); 153 ASSERT(m_transaction); 154 ASSERT(!m_pendingCursor); 155 ASSERT(cursor == getResultCursor()); 156 157 m_hasPendingActivity = true; 158 m_pendingCursor = cursor; 159 m_result.clear(); 160 m_readyState = PENDING; 161 m_error.clear(); 162 m_transaction->registerRequest(this); 163 } 164 165 IDBCursor* IDBRequest::getResultCursor() const 166 { 167 if (!m_result) 168 return 0; 169 if (m_result->type() == IDBAny::IDBCursorType) 170 return m_result->idbCursor(); 171 if (m_result->type() == IDBAny::IDBCursorWithValueType) 172 return m_result->idbCursorWithValue(); 173 return 0; 174 } 175 176 void IDBRequest::setResultCursor(PassRefPtr<IDBCursor> cursor, PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> value) 177 { 178 ASSERT(m_readyState == PENDING); 179 m_cursorKey = key; 180 m_cursorPrimaryKey = primaryKey; 181 m_cursorValue = value; 182 183 onSuccessInternal(IDBAny::create(cursor)); 184 } 185 186 void IDBRequest::checkForReferenceCycle() 187 { 188 // If this request and its cursor have the only references 189 // to each other, then explicitly break the cycle. 190 IDBCursor* cursor = getResultCursor(); 191 if (!cursor || cursor->request() != this) 192 return; 193 194 if (!hasOneRef() || !cursor->hasOneRef()) 195 return; 196 197 m_result.clear(); 198 } 199 200 bool IDBRequest::shouldEnqueueEvent() const 201 { 202 if (m_contextStopped || !executionContext()) 203 return false; 204 ASSERT(m_readyState == PENDING || m_readyState == DONE); 205 if (m_requestAborted) 206 return false; 207 ASSERT(m_readyState == PENDING); 208 ASSERT(!m_error && !m_result); 209 return true; 210 } 211 212 void IDBRequest::onError(PassRefPtr<DOMError> error) 213 { 214 IDB_TRACE("IDBRequest::onError()"); 215 if (!shouldEnqueueEvent()) 216 return; 217 218 m_error = error; 219 m_pendingCursor.clear(); 220 enqueueEvent(Event::createCancelableBubble(EventTypeNames::error)); 221 } 222 223 void IDBRequest::onSuccess(const Vector<String>& stringList) 224 { 225 IDB_TRACE("IDBRequest::onSuccess(StringList)"); 226 if (!shouldEnqueueEvent()) 227 return; 228 229 RefPtr<DOMStringList> domStringList = DOMStringList::create(); 230 for (size_t i = 0; i < stringList.size(); ++i) 231 domStringList->append(stringList[i]); 232 onSuccessInternal(IDBAny::create(domStringList.release())); 233 } 234 235 void IDBRequest::onSuccess(PassOwnPtr<blink::WebIDBCursor> backend, PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> value) 236 { 237 IDB_TRACE("IDBRequest::onSuccess(IDBCursor)"); 238 if (!shouldEnqueueEvent()) 239 return; 240 241 ASSERT(!m_pendingCursor); 242 RefPtr<IDBCursor> cursor; 243 switch (m_cursorType) { 244 case IndexedDB::CursorKeyOnly: 245 cursor = IDBCursor::create(backend, m_cursorDirection, this, m_source.get(), m_transaction.get()); 246 break; 247 case IndexedDB::CursorKeyAndValue: 248 cursor = IDBCursorWithValue::create(backend, m_cursorDirection, this, m_source.get(), m_transaction.get()); 249 break; 250 default: 251 ASSERT_NOT_REACHED(); 252 } 253 setResultCursor(cursor, key, primaryKey, value); 254 } 255 256 void IDBRequest::onSuccess(PassRefPtr<IDBKey> idbKey) 257 { 258 IDB_TRACE("IDBRequest::onSuccess(IDBKey)"); 259 if (!shouldEnqueueEvent()) 260 return; 261 262 if (idbKey && idbKey->isValid()) 263 onSuccessInternal(IDBAny::create(idbKey)); 264 else 265 onSuccessInternal(IDBAny::createUndefined()); 266 } 267 268 void IDBRequest::onSuccess(PassRefPtr<SharedBuffer> valueBuffer) 269 { 270 IDB_TRACE("IDBRequest::onSuccess(SharedBuffer)"); 271 if (!shouldEnqueueEvent()) 272 return; 273 274 if (m_pendingCursor) { 275 // Value should be null, signifying the end of the cursor's range. 276 ASSERT(!valueBuffer.get()); 277 m_pendingCursor->close(); 278 m_pendingCursor.clear(); 279 } 280 281 onSuccessInternal(IDBAny::create(valueBuffer)); 282 } 283 284 #ifndef NDEBUG 285 static PassRefPtr<IDBObjectStore> effectiveObjectStore(PassRefPtr<IDBAny> source) 286 { 287 if (source->type() == IDBAny::IDBObjectStoreType) 288 return source->idbObjectStore(); 289 if (source->type() == IDBAny::IDBIndexType) 290 return source->idbIndex()->objectStore(); 291 292 ASSERT_NOT_REACHED(); 293 return 0; 294 } 295 #endif 296 297 void IDBRequest::onSuccess(PassRefPtr<SharedBuffer> prpValueBuffer, PassRefPtr<IDBKey> prpPrimaryKey, const IDBKeyPath& keyPath) 298 { 299 IDB_TRACE("IDBRequest::onSuccess(SharedBuffer, IDBKey, IDBKeyPath)"); 300 if (!shouldEnqueueEvent()) 301 return; 302 303 #ifndef NDEBUG 304 ASSERT(keyPath == effectiveObjectStore(m_source)->metadata().keyPath); 305 #endif 306 307 RefPtr<SharedBuffer> valueBuffer = prpValueBuffer; 308 RefPtr<IDBKey> primaryKey = prpPrimaryKey; 309 310 #ifndef NDEBUG 311 assertPrimaryKeyValidOrInjectable(&m_requestState, valueBuffer, primaryKey, keyPath); 312 #endif 313 314 onSuccessInternal(IDBAny::create(valueBuffer, primaryKey, keyPath)); 315 } 316 317 void IDBRequest::onSuccess(int64_t value) 318 { 319 IDB_TRACE("IDBRequest::onSuccess(int64_t)"); 320 if (!shouldEnqueueEvent()) 321 return; 322 onSuccessInternal(IDBAny::create(value)); 323 } 324 325 void IDBRequest::onSuccess() 326 { 327 IDB_TRACE("IDBRequest::onSuccess()"); 328 if (!shouldEnqueueEvent()) 329 return; 330 onSuccessInternal(IDBAny::createUndefined()); 331 } 332 333 void IDBRequest::onSuccessInternal(PassRefPtr<IDBAny> result) 334 { 335 ASSERT(!m_contextStopped); 336 ASSERT(!m_pendingCursor); 337 setResult(result); 338 enqueueEvent(Event::create(EventTypeNames::success)); 339 } 340 341 void IDBRequest::setResult(PassRefPtr<IDBAny> result) 342 { 343 m_result = result; 344 m_resultDirty = true; 345 } 346 347 void IDBRequest::onSuccess(PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> value) 348 { 349 IDB_TRACE("IDBRequest::onSuccess(key, primaryKey, value)"); 350 if (!shouldEnqueueEvent()) 351 return; 352 353 ASSERT(m_pendingCursor); 354 setResultCursor(m_pendingCursor.release(), key, primaryKey, value); 355 } 356 357 bool IDBRequest::hasPendingActivity() const 358 { 359 // FIXME: In an ideal world, we should return true as long as anyone has a or can 360 // get a handle to us and we have event listeners. This is order to handle 361 // user generated events properly. 362 return m_hasPendingActivity && !m_contextStopped; 363 } 364 365 void IDBRequest::stop() 366 { 367 if (m_contextStopped) 368 return; 369 370 m_contextStopped = true; 371 m_requestState.clear(); 372 373 RefPtr<IDBRequest> protect(this); 374 375 if (m_readyState == PENDING) { 376 m_readyState = EarlyDeath; 377 if (m_transaction) { 378 m_transaction->unregisterRequest(this); 379 m_transaction.clear(); 380 } 381 } 382 383 m_enqueuedEvents.clear(); 384 } 385 386 const AtomicString& IDBRequest::interfaceName() const 387 { 388 return EventTargetNames::IDBRequest; 389 } 390 391 ExecutionContext* IDBRequest::executionContext() const 392 { 393 return ActiveDOMObject::executionContext(); 394 } 395 396 bool IDBRequest::dispatchEvent(PassRefPtr<Event> event) 397 { 398 IDB_TRACE("IDBRequest::dispatchEvent"); 399 if (m_contextStopped || !executionContext()) 400 return false; 401 ASSERT(m_requestState.isValid()); 402 ASSERT(m_readyState == PENDING); 403 ASSERT(m_hasPendingActivity); 404 ASSERT(m_enqueuedEvents.size()); 405 ASSERT(event->target() == this); 406 407 DOMRequestState::Scope scope(m_requestState); 408 409 if (event->type() != EventTypeNames::blocked) 410 m_readyState = DONE; 411 dequeueEvent(event.get()); 412 413 Vector<RefPtr<EventTarget> > targets; 414 targets.append(this); 415 if (m_transaction && !m_preventPropagation) { 416 targets.append(m_transaction); 417 // If there ever are events that are associated with a database but 418 // that do not have a transaction, then this will not work and we need 419 // this object to actually hold a reference to the database (to ensure 420 // it stays alive). 421 targets.append(m_transaction->db()); 422 } 423 424 // Cursor properties should not be updated until the success event is being dispatched. 425 RefPtr<IDBCursor> cursorToNotify; 426 if (event->type() == EventTypeNames::success) { 427 cursorToNotify = getResultCursor(); 428 if (cursorToNotify) 429 cursorToNotify->setValueReady(m_cursorKey.release(), m_cursorPrimaryKey.release(), m_cursorValue.release()); 430 } 431 432 if (event->type() == EventTypeNames::upgradeneeded) { 433 ASSERT(!m_didFireUpgradeNeededEvent); 434 m_didFireUpgradeNeededEvent = true; 435 } 436 437 // FIXME: When we allow custom event dispatching, this will probably need to change. 438 ASSERT_WITH_MESSAGE(event->type() == EventTypeNames::success || event->type() == EventTypeNames::error || event->type() == EventTypeNames::blocked || event->type() == EventTypeNames::upgradeneeded, "event type was %s", event->type().string().utf8().data()); 439 const bool setTransactionActive = m_transaction && (event->type() == EventTypeNames::success || event->type() == EventTypeNames::upgradeneeded || (event->type() == EventTypeNames::error && !m_requestAborted)); 440 441 if (setTransactionActive) 442 m_transaction->setActive(true); 443 444 bool dontPreventDefault = IDBEventDispatcher::dispatch(event.get(), targets); 445 446 if (m_transaction) { 447 if (m_readyState == DONE) 448 m_transaction->unregisterRequest(this); 449 450 // Possibly abort the transaction. This must occur after unregistering (so this request 451 // doesn't receive a second error) and before deactivating (which might trigger commit). 452 if (event->type() == EventTypeNames::error && dontPreventDefault && !m_requestAborted) { 453 m_transaction->setError(m_error); 454 m_transaction->abort(IGNORE_EXCEPTION); 455 } 456 457 // If this was the last request in the transaction's list, it may commit here. 458 if (setTransactionActive) 459 m_transaction->setActive(false); 460 } 461 462 if (cursorToNotify) 463 cursorToNotify->postSuccessHandlerCallback(); 464 465 // An upgradeneeded event will always be followed by a success or error event, so must 466 // be kept alive. 467 if (m_readyState == DONE && event->type() != EventTypeNames::upgradeneeded) 468 m_hasPendingActivity = false; 469 470 return dontPreventDefault; 471 } 472 473 void IDBRequest::uncaughtExceptionInEventHandler() 474 { 475 if (m_transaction && !m_requestAborted) { 476 m_transaction->setError(DOMError::create(AbortError, "Uncaught exception in event handler.")); 477 m_transaction->abort(IGNORE_EXCEPTION); 478 } 479 } 480 481 void IDBRequest::transactionDidFinishAndDispatch() 482 { 483 ASSERT(m_transaction); 484 ASSERT(m_transaction->isVersionChange()); 485 ASSERT(m_didFireUpgradeNeededEvent); 486 ASSERT(m_readyState == DONE); 487 ASSERT(executionContext()); 488 m_transaction.clear(); 489 490 if (m_contextStopped) 491 return; 492 493 m_readyState = PENDING; 494 } 495 496 void IDBRequest::enqueueEvent(PassRefPtr<Event> event) 497 { 498 ASSERT(m_readyState == PENDING || m_readyState == DONE); 499 500 if (m_contextStopped || !executionContext()) 501 return; 502 503 ASSERT_WITH_MESSAGE(m_readyState == PENDING || m_didFireUpgradeNeededEvent, "When queueing event %s, m_readyState was %d", event->type().string().utf8().data(), m_readyState); 504 505 EventQueue* eventQueue = executionContext()->eventQueue(); 506 event->setTarget(this); 507 508 // Keep track of enqueued events in case we need to abort prior to dispatch, 509 // in which case these must be cancelled. If the events not dispatched for 510 // other reasons they must be removed from this list via dequeueEvent(). 511 if (eventQueue->enqueueEvent(event.get())) 512 m_enqueuedEvents.append(event); 513 } 514 515 void IDBRequest::dequeueEvent(Event* event) 516 { 517 for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) { 518 if (m_enqueuedEvents[i].get() == event) 519 m_enqueuedEvents.remove(i); 520 } 521 } 522 523 } // namespace WebCore 524