Bumping gaia.json for 2 gaia revision(s) a=gaia-bump
[gecko.git] / dom / indexedDB / AsyncConnectionHelper.cpp
blobe546d8a5a34de3ee0429a003dc963afc2f52f54f
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "base/basictypes.h"
9 #include "AsyncConnectionHelper.h"
11 #include "mozilla/dom/quota/QuotaManager.h"
12 #include "mozilla/storage.h"
13 #include "nsComponentManagerUtils.h"
14 #include "nsContentUtils.h"
15 #include "nsProxyRelease.h"
16 #include "nsThreadUtils.h"
17 #include "nsWrapperCacheInlines.h"
19 #include "IDBEvents.h"
20 #include "IDBTransaction.h"
21 #include "IndexedDatabaseManager.h"
22 #include "ProfilerHelpers.h"
23 #include "ReportInternalError.h"
24 #include "TransactionThreadPool.h"
26 #include "ipc/IndexedDBChild.h"
27 #include "ipc/IndexedDBParent.h"
29 using namespace mozilla;
30 USING_INDEXEDDB_NAMESPACE
31 using mozilla::dom::quota::QuotaManager;
33 namespace {
35 IDBTransaction* gCurrentTransaction = nullptr;
37 const uint32_t kProgressHandlerGranularity = 1000;
39 class MOZ_STACK_CLASS TransactionPoolEventTarget : public StackBasedEventTarget
41 public:
42 NS_DECL_NSIEVENTTARGET
44 TransactionPoolEventTarget(IDBTransaction* aTransaction)
45 : mTransaction(aTransaction)
46 { }
48 private:
49 IDBTransaction* mTransaction;
52 // This inline is just so that we always clear aBuffers appropriately even if
53 // something fails.
54 inline
55 nsresult
56 ConvertCloneReadInfosToArrayInternal(
57 JSContext* aCx,
58 nsTArray<StructuredCloneReadInfo>& aReadInfos,
59 JS::MutableHandle<JS::Value> aResult)
61 JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, 0));
62 if (!array) {
63 IDB_WARNING("Failed to make array!");
64 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
67 if (!aReadInfos.IsEmpty()) {
68 if (!JS_SetArrayLength(aCx, array, uint32_t(aReadInfos.Length()))) {
69 IDB_WARNING("Failed to set array length!");
70 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
73 for (uint32_t index = 0, count = aReadInfos.Length(); index < count;
74 index++) {
75 StructuredCloneReadInfo& readInfo = aReadInfos[index];
77 JS::Rooted<JS::Value> val(aCx);
78 if (!IDBObjectStore::DeserializeValue(aCx, readInfo, &val)) {
79 NS_WARNING("Failed to decode!");
80 return NS_ERROR_DOM_DATA_CLONE_ERR;
83 if (!JS_SetElement(aCx, array, index, val)) {
84 IDB_WARNING("Failed to set array element!");
85 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
90 aResult.setObject(*array);
91 return NS_OK;
94 } // anonymous namespace
96 HelperBase::~HelperBase()
98 if (!NS_IsMainThread()) {
99 IDBRequest* request;
100 mRequest.forget(&request);
102 if (request) {
103 nsCOMPtr<nsIThread> mainThread;
104 NS_GetMainThread(getter_AddRefs(mainThread));
105 NS_WARN_IF_FALSE(mainThread, "Couldn't get the main thread!");
107 if (mainThread) {
108 NS_ProxyRelease(mainThread, static_cast<EventTarget*>(request));
114 nsresult
115 HelperBase::WrapNative(JSContext* aCx,
116 nsISupports* aNative,
117 JS::MutableHandle<JS::Value> aResult)
119 NS_ASSERTION(aCx, "Null context!");
120 NS_ASSERTION(aNative, "Null pointer!");
121 NS_ASSERTION(aResult.address(), "Null pointer!");
122 NS_ASSERTION(mRequest, "Null request!");
124 nsresult rv = nsContentUtils::WrapNative(aCx, aNative, aResult);
125 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
127 return NS_OK;
130 void
131 HelperBase::ReleaseMainThreadObjects()
133 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
135 mRequest = nullptr;
138 AsyncConnectionHelper::AsyncConnectionHelper(IDBDatabase* aDatabase,
139 IDBRequest* aRequest)
140 : HelperBase(aRequest),
141 mDatabase(aDatabase),
142 mResultCode(NS_OK),
143 mDispatched(false)
145 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
148 AsyncConnectionHelper::AsyncConnectionHelper(IDBTransaction* aTransaction,
149 IDBRequest* aRequest)
150 : HelperBase(aRequest),
151 mDatabase(aTransaction->mDatabase),
152 mTransaction(aTransaction),
153 mResultCode(NS_OK),
154 mDispatched(false)
156 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
159 AsyncConnectionHelper::~AsyncConnectionHelper()
161 if (!NS_IsMainThread()) {
162 IDBDatabase* database;
163 mDatabase.forget(&database);
165 IDBTransaction* transaction;
166 mTransaction.forget(&transaction);
168 nsCOMPtr<nsIThread> mainThread;
169 NS_GetMainThread(getter_AddRefs(mainThread));
170 NS_WARN_IF_FALSE(mainThread, "Couldn't get the main thread!");
172 if (mainThread) {
173 if (database) {
174 NS_ProxyRelease(mainThread, static_cast<IDBWrapperCache*>(database));
176 if (transaction) {
177 NS_ProxyRelease(mainThread, static_cast<IDBWrapperCache*>(transaction));
182 NS_ASSERTION(!mOldProgressHandler, "Should not have anything here!");
185 NS_IMPL_ISUPPORTS(AsyncConnectionHelper, nsIRunnable,
186 mozIStorageProgressHandler)
188 NS_IMETHODIMP
189 AsyncConnectionHelper::Run()
191 if (NS_IsMainThread()) {
192 PROFILER_MAIN_THREAD_LABEL("AsyncConnectionHelper", "Run",
193 js::ProfileEntry::Category::STORAGE);
195 if (mTransaction &&
196 mTransaction->IsAborted()) {
197 // Always fire a "error" event with ABORT_ERR if the transaction was
198 // aborted, even if the request succeeded or failed with another error.
199 mResultCode = NS_ERROR_DOM_INDEXEDDB_ABORT_ERR;
202 IDBTransaction* oldTransaction = gCurrentTransaction;
203 gCurrentTransaction = mTransaction;
205 ChildProcessSendResult sendResult =
206 IndexedDatabaseManager::IsMainProcess() ?
207 MaybeSendResponseToChildProcess(mResultCode) :
208 Success_NotSent;
210 switch (sendResult) {
211 case Success_Sent: {
212 if (mRequest) {
213 mRequest->NotifyHelperSentResultsToChildProcess(NS_OK);
215 break;
218 case Success_NotSent: {
219 if (mRequest) {
220 nsresult rv = mRequest->NotifyHelperCompleted(this);
221 if (NS_SUCCEEDED(mResultCode) && NS_FAILED(rv)) {
222 mResultCode = rv;
225 IDB_PROFILER_MARK("IndexedDB Request %llu: Running main thread "
226 "response (rv = %lu)",
227 "IDBRequest[%llu] MT Done",
228 mRequest->GetSerialNumber(), mResultCode);
231 // Call OnError if the database had an error or if the OnSuccess
232 // handler has an error.
233 if (NS_FAILED(mResultCode) ||
234 NS_FAILED((mResultCode = OnSuccess()))) {
235 OnError();
237 break;
240 case Success_ActorDisconnected: {
241 // Nothing needs to be done here.
242 break;
245 case Error: {
246 IDB_WARNING("MaybeSendResultsToChildProcess failed!");
247 mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
248 if (mRequest) {
249 mRequest->NotifyHelperSentResultsToChildProcess(mResultCode);
251 break;
254 default:
255 MOZ_CRASH("Unknown value for ChildProcessSendResult!");
258 NS_ASSERTION(gCurrentTransaction == mTransaction, "Should be unchanged!");
259 gCurrentTransaction = oldTransaction;
261 if (mDispatched && mTransaction) {
262 mTransaction->OnRequestFinished();
265 ReleaseMainThreadObjects();
267 NS_ASSERTION(!(mDatabase || mTransaction || mRequest), "Subclass didn't "
268 "call AsyncConnectionHelper::ReleaseMainThreadObjects!");
270 return NS_OK;
273 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
275 PROFILER_LABEL("AsyncConnectionHelper", "Run",
276 js::ProfileEntry::Category::STORAGE);
278 IDB_PROFILER_MARK_IF(mRequest,
279 "IndexedDB Request %llu: Beginning database work",
280 "IDBRequest[%llu] DT Start",
281 mRequest->GetSerialNumber());
283 nsresult rv = NS_OK;
284 nsCOMPtr<mozIStorageConnection> connection;
286 if (mTransaction) {
287 rv = mTransaction->GetOrCreateConnection(getter_AddRefs(connection));
288 if (NS_SUCCEEDED(rv)) {
289 NS_ASSERTION(connection, "This should never be null!");
293 bool setProgressHandler = false;
294 if (connection) {
295 rv = connection->SetProgressHandler(kProgressHandlerGranularity, this,
296 getter_AddRefs(mOldProgressHandler));
297 NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "SetProgressHandler failed!");
298 if (NS_SUCCEEDED(rv)) {
299 setProgressHandler = true;
303 if (NS_SUCCEEDED(rv)) {
304 bool hasSavepoint = false;
305 if (mDatabase) {
306 QuotaManager::SetCurrentWindow(mDatabase->GetOwner());
308 // Make the first savepoint.
309 if (mTransaction) {
310 if (!(hasSavepoint = mTransaction->StartSavepoint())) {
311 NS_WARNING("Failed to make savepoint!");
316 mResultCode = DoDatabaseWork(connection);
318 if (mDatabase) {
319 // Release or roll back the savepoint depending on the error code.
320 if (hasSavepoint) {
321 NS_ASSERTION(mTransaction, "Huh?!");
322 if (NS_SUCCEEDED(mResultCode)) {
323 mTransaction->ReleaseSavepoint();
325 else {
326 mTransaction->RollbackSavepoint();
330 // Don't unset this until we're sure that all SQLite activity has
331 // completed!
332 QuotaManager::SetCurrentWindow(nullptr);
335 else {
336 // NS_ERROR_NOT_AVAILABLE is our special code for "database is invalidated"
337 // and we should fail with RECOVERABLE_ERR.
338 if (rv == NS_ERROR_NOT_AVAILABLE) {
339 mResultCode = NS_ERROR_DOM_INDEXEDDB_RECOVERABLE_ERR;
341 else {
342 IDB_REPORT_INTERNAL_ERR();
343 mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
347 if (setProgressHandler) {
348 nsCOMPtr<mozIStorageProgressHandler> handler;
349 rv = connection->RemoveProgressHandler(getter_AddRefs(handler));
350 NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "RemoveProgressHandler failed!");
351 #ifdef DEBUG
352 if (NS_SUCCEEDED(rv)) {
353 NS_ASSERTION(SameCOMIdentity(handler, static_cast<nsIRunnable*>(this)),
354 "Mismatch!");
356 #endif
359 IDB_PROFILER_MARK_IF(mRequest,
360 "IndexedDB Request %llu: Finished database work "
361 "(rv = %lu)",
362 "IDBRequest[%llu] DT Done", mRequest->GetSerialNumber(),
363 mResultCode);
365 return NS_DispatchToMainThread(this);
368 NS_IMETHODIMP
369 AsyncConnectionHelper::OnProgress(mozIStorageConnection* aConnection,
370 bool* _retval)
372 if (mDatabase && mDatabase->IsInvalidated()) {
373 // Someone is trying to delete the database file. Exit lightningfast!
374 *_retval = true;
375 return NS_OK;
378 if (mOldProgressHandler) {
379 return mOldProgressHandler->OnProgress(aConnection, _retval);
382 *_retval = false;
383 return NS_OK;
386 nsresult
387 AsyncConnectionHelper::Dispatch(nsIEventTarget* aTarget)
389 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
391 nsresult rv = Init();
392 if (NS_FAILED(rv)) {
393 return rv;
396 rv = aTarget->Dispatch(this, NS_DISPATCH_NORMAL);
397 NS_ENSURE_SUCCESS(rv, rv);
399 if (mTransaction) {
400 mTransaction->OnNewRequest();
403 mDispatched = true;
405 return NS_OK;
408 nsresult
409 AsyncConnectionHelper::DispatchToTransactionPool()
411 NS_ASSERTION(mTransaction, "Only ok to call this with a transaction!");
412 TransactionPoolEventTarget target(mTransaction);
413 return Dispatch(&target);
416 // static
417 void
418 AsyncConnectionHelper::SetCurrentTransaction(IDBTransaction* aTransaction)
420 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
421 NS_ASSERTION(!aTransaction || !gCurrentTransaction,
422 "Stepping on another transaction!");
424 gCurrentTransaction = aTransaction;
427 // static
428 IDBTransaction*
429 AsyncConnectionHelper::GetCurrentTransaction()
431 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
433 return gCurrentTransaction;
436 nsresult
437 AsyncConnectionHelper::Init()
439 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
441 return NS_OK;
444 already_AddRefed<nsIDOMEvent>
445 AsyncConnectionHelper::CreateSuccessEvent(mozilla::dom::EventTarget* aOwner)
447 return CreateGenericEvent(mRequest, NS_LITERAL_STRING(SUCCESS_EVT_STR),
448 eDoesNotBubble, eNotCancelable);
451 nsresult
452 AsyncConnectionHelper::OnSuccess()
454 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
455 NS_ASSERTION(mRequest, "Null request!");
457 PROFILER_MAIN_THREAD_LABEL("AsyncConnectionHelper", "OnSuccess",
458 js::ProfileEntry::Category::STORAGE);
460 nsRefPtr<nsIDOMEvent> event = CreateSuccessEvent(mRequest);
461 if (!event) {
462 IDB_WARNING("Failed to create event!");
463 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
466 bool dummy;
467 nsresult rv = mRequest->DispatchEvent(event, &dummy);
468 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
470 WidgetEvent* internalEvent = event->GetInternalNSEvent();
471 NS_ASSERTION(internalEvent, "This should never be null!");
473 NS_ASSERTION(!mTransaction ||
474 mTransaction->IsOpen() ||
475 mTransaction->IsAborted(),
476 "How else can this be closed?!");
478 if (internalEvent->mFlags.mExceptionHasBeenRisen &&
479 mTransaction &&
480 mTransaction->IsOpen()) {
481 rv = mTransaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
482 NS_ENSURE_SUCCESS(rv, rv);
485 return NS_OK;
488 void
489 AsyncConnectionHelper::OnError()
491 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
492 NS_ASSERTION(mRequest, "Null request!");
494 PROFILER_MAIN_THREAD_LABEL("AsyncConnectionHelper", "OnError",
495 js::ProfileEntry::Category::STORAGE);
497 // Make an error event and fire it at the target.
498 nsRefPtr<nsIDOMEvent> event =
499 CreateGenericEvent(mRequest, NS_LITERAL_STRING(ERROR_EVT_STR), eDoesBubble,
500 eCancelable);
501 if (!event) {
502 NS_ERROR("Failed to create event!");
503 return;
506 bool doDefault;
507 nsresult rv = mRequest->DispatchEvent(event, &doDefault);
508 if (NS_SUCCEEDED(rv)) {
509 NS_ASSERTION(!mTransaction ||
510 mTransaction->IsOpen() ||
511 mTransaction->IsAborted(),
512 "How else can this be closed?!");
514 WidgetEvent* internalEvent = event->GetInternalNSEvent();
515 NS_ASSERTION(internalEvent, "This should never be null!");
517 if (internalEvent->mFlags.mExceptionHasBeenRisen &&
518 mTransaction &&
519 mTransaction->IsOpen() &&
520 NS_FAILED(mTransaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR))) {
521 NS_WARNING("Failed to abort transaction!");
524 if (doDefault &&
525 mTransaction &&
526 mTransaction->IsOpen() &&
527 NS_FAILED(mTransaction->Abort(mRequest))) {
528 NS_WARNING("Failed to abort transaction!");
531 else {
532 NS_WARNING("DispatchEvent failed!");
536 nsresult
537 AsyncConnectionHelper::GetSuccessResult(JSContext* aCx,
538 JS::MutableHandle<JS::Value> aVal)
540 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
542 aVal.setUndefined();
543 return NS_OK;
546 void
547 AsyncConnectionHelper::ReleaseMainThreadObjects()
549 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
551 mDatabase = nullptr;
552 mTransaction = nullptr;
554 HelperBase::ReleaseMainThreadObjects();
557 AsyncConnectionHelper::ChildProcessSendResult
558 AsyncConnectionHelper::MaybeSendResponseToChildProcess(nsresult aResultCode)
560 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
561 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
563 // If there's no request, there could never have been an actor, and so there
564 // is nothing to do.
565 if (!mRequest) {
566 return Success_NotSent;
569 IDBTransaction* trans = GetCurrentTransaction();
570 // We may not have a transaction, e.g. for deleteDatabase
571 if (!trans) {
572 return Success_NotSent;
575 // Are we shutting down the child?
576 IndexedDBDatabaseParent* dbActor = trans->Database()->GetActorParent();
577 if (dbActor && dbActor->IsDisconnected()) {
578 return Success_ActorDisconnected;
581 IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
582 if (!actor) {
583 return Success_NotSent;
586 IDB_PROFILER_MARK("IndexedDB Request %llu: Sending response to child "
587 "process (rv = %lu)",
588 "IDBRequest[%llu] MT Done",
589 mRequest->GetSerialNumber(), aResultCode);
591 return SendResponseToChildProcess(aResultCode);
594 nsresult
595 AsyncConnectionHelper::OnParentProcessRequestComplete(
596 const ResponseValue& aResponseValue)
598 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
600 if (aResponseValue.type() == ResponseValue::Tnsresult) {
601 NS_ASSERTION(NS_FAILED(aResponseValue.get_nsresult()), "Huh?");
602 SetError(aResponseValue.get_nsresult());
604 else {
605 nsresult rv = UnpackResponseFromParentProcess(aResponseValue);
606 NS_ENSURE_SUCCESS(rv, rv);
609 return Run();
612 // static
613 nsresult
614 AsyncConnectionHelper::ConvertToArrayAndCleanup(
615 JSContext* aCx,
616 nsTArray<StructuredCloneReadInfo>& aReadInfos,
617 JS::MutableHandle<JS::Value> aResult)
619 NS_ASSERTION(aCx, "Null context!");
620 NS_ASSERTION(aResult.address(), "Null pointer!");
622 nsresult rv = ConvertCloneReadInfosToArrayInternal(aCx, aReadInfos, aResult);
624 for (uint32_t index = 0; index < aReadInfos.Length(); index++) {
625 aReadInfos[index].mCloneBuffer.clear();
627 aReadInfos.Clear();
629 return rv;
632 NS_IMETHODIMP_(MozExternalRefCountType)
633 StackBasedEventTarget::AddRef()
635 NS_NOTREACHED("Don't call me!");
636 return 2;
639 NS_IMETHODIMP_(MozExternalRefCountType)
640 StackBasedEventTarget::Release()
642 NS_NOTREACHED("Don't call me!");
643 return 1;
646 NS_IMETHODIMP
647 StackBasedEventTarget::QueryInterface(REFNSIID aIID,
648 void** aInstancePtr)
650 NS_NOTREACHED("Don't call me!");
651 return NS_NOINTERFACE;
654 NS_IMETHODIMP
655 ImmediateRunEventTarget::Dispatch(nsIRunnable* aRunnable,
656 uint32_t aFlags)
658 NS_ASSERTION(aRunnable, "Null pointer!");
660 nsCOMPtr<nsIRunnable> runnable(aRunnable);
661 DebugOnly<nsresult> rv =
662 runnable->Run();
663 MOZ_ASSERT(NS_SUCCEEDED(rv));
664 return NS_OK;
667 NS_IMETHODIMP
668 ImmediateRunEventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread)
670 *aIsOnCurrentThread = true;
671 return NS_OK;
674 NS_IMETHODIMP
675 TransactionPoolEventTarget::Dispatch(nsIRunnable* aRunnable,
676 uint32_t aFlags)
678 NS_ASSERTION(aRunnable, "Null pointer!");
679 NS_ASSERTION(aFlags == NS_DISPATCH_NORMAL, "Unsupported!");
681 TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate();
682 NS_ENSURE_TRUE(pool, NS_ERROR_UNEXPECTED);
684 nsresult rv = pool->Dispatch(mTransaction, aRunnable, false, nullptr);
685 NS_ENSURE_SUCCESS(rv, rv);
687 return NS_OK;
690 NS_IMETHODIMP
691 TransactionPoolEventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread)
693 *aIsOnCurrentThread = false;
694 return NS_OK;
697 NS_IMETHODIMP
698 NoDispatchEventTarget::Dispatch(nsIRunnable* aRunnable,
699 uint32_t aFlags)
701 nsCOMPtr<nsIRunnable> runnable = aRunnable;
702 return NS_OK;
705 NS_IMETHODIMP
706 NoDispatchEventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread)
708 *aIsOnCurrentThread = true;
709 return NS_OK;