Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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 "IDBDatabase.h"
11 #include "mozilla/EventDispatcher.h"
12 #include "mozilla/Mutex.h"
13 #include "mozilla/storage.h"
14 #include "mozilla/dom/ContentParent.h"
15 #include "mozilla/dom/DOMStringList.h"
16 #include "mozilla/dom/DOMStringListBinding.h"
17 #include "mozilla/dom/quota/Client.h"
18 #include "mozilla/dom/quota/QuotaManager.h"
19 #include "nsJSUtils.h"
20 #include "nsProxyRelease.h"
21 #include "nsThreadUtils.h"
23 #include "AsyncConnectionHelper.h"
24 #include "DatabaseInfo.h"
25 #include "IDBEvents.h"
26 #include "IDBFactory.h"
27 #include "IDBFileHandle.h"
28 #include "IDBIndex.h"
29 #include "IDBObjectStore.h"
30 #include "IDBTransaction.h"
31 #include "IDBFactory.h"
32 #include "ProfilerHelpers.h"
33 #include "ReportInternalError.h"
34 #include "TransactionThreadPool.h"
36 #include "ipc/IndexedDBChild.h"
37 #include "ipc/IndexedDBParent.h"
39 #include "mozilla/dom/IDBDatabaseBinding.h"
41 USING_INDEXEDDB_NAMESPACE
42 using mozilla::dom::ContentParent;
43 using mozilla::dom::quota::AssertIsOnIOThread;
44 using mozilla::dom::quota::Client;
45 using mozilla::dom::quota::QuotaManager;
46 using mozilla::ErrorResult;
47 using namespace mozilla;
48 using namespace mozilla::dom;
50 namespace {
52 class NoRequestDatabaseHelper : public AsyncConnectionHelper
53 {
54 public:
55 NoRequestDatabaseHelper(IDBTransaction* aTransaction)
56 : AsyncConnectionHelper(aTransaction, nullptr)
57 {
58 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
59 NS_ASSERTION(aTransaction, "Null transaction!");
60 }
62 virtual ChildProcessSendResult
63 SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE;
65 virtual nsresult
66 UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
67 MOZ_OVERRIDE;
69 virtual nsresult OnSuccess() MOZ_OVERRIDE;
71 virtual void OnError() MOZ_OVERRIDE;
72 };
74 class CreateObjectStoreHelper : public NoRequestDatabaseHelper
75 {
76 public:
77 CreateObjectStoreHelper(IDBTransaction* aTransaction,
78 IDBObjectStore* aObjectStore)
79 : NoRequestDatabaseHelper(aTransaction), mObjectStore(aObjectStore)
80 { }
82 virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection)
83 MOZ_OVERRIDE;
85 virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE;
87 private:
88 nsRefPtr<IDBObjectStore> mObjectStore;
89 };
91 class DeleteObjectStoreHelper : public NoRequestDatabaseHelper
92 {
93 public:
94 DeleteObjectStoreHelper(IDBTransaction* aTransaction,
95 int64_t aObjectStoreId)
96 : NoRequestDatabaseHelper(aTransaction), mObjectStoreId(aObjectStoreId)
97 { }
99 virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection)
100 MOZ_OVERRIDE;
102 private:
103 // In-params.
104 int64_t mObjectStoreId;
105 };
107 class CreateFileHelper : public AsyncConnectionHelper
108 {
109 public:
110 CreateFileHelper(IDBDatabase* aDatabase,
111 IDBRequest* aRequest,
112 const nsAString& aName,
113 const nsAString& aType)
114 : AsyncConnectionHelper(aDatabase, aRequest),
115 mName(aName), mType(aType)
116 { }
118 ~CreateFileHelper()
119 { }
121 nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
122 nsresult GetSuccessResult(JSContext* aCx,
123 JS::MutableHandle<JS::Value> aVal);
124 void ReleaseMainThreadObjects()
125 {
126 mFileInfo = nullptr;
127 AsyncConnectionHelper::ReleaseMainThreadObjects();
128 }
130 virtual ChildProcessSendResult SendResponseToChildProcess(
131 nsresult aResultCode)
132 MOZ_OVERRIDE
133 {
134 return Success_NotSent;
135 }
137 virtual nsresult UnpackResponseFromParentProcess(
138 const ResponseValue& aResponseValue)
139 MOZ_OVERRIDE
140 {
141 MOZ_CRASH("Should never get here!");
142 }
144 private:
145 // In-params.
146 nsString mName;
147 nsString mType;
149 // Out-params.
150 nsRefPtr<FileInfo> mFileInfo;
151 };
153 class MOZ_STACK_CLASS AutoRemoveObjectStore
154 {
155 public:
156 AutoRemoveObjectStore(DatabaseInfo* aInfo, const nsAString& aName)
157 : mInfo(aInfo), mName(aName)
158 { }
160 ~AutoRemoveObjectStore()
161 {
162 if (mInfo) {
163 mInfo->RemoveObjectStore(mName);
164 }
165 }
167 void forget()
168 {
169 mInfo = nullptr;
170 }
172 private:
173 DatabaseInfo* mInfo;
174 nsString mName;
175 };
177 } // anonymous namespace
179 // static
180 already_AddRefed<IDBDatabase>
181 IDBDatabase::Create(IDBWrapperCache* aOwnerCache,
182 IDBFactory* aFactory,
183 already_AddRefed<DatabaseInfo> aDatabaseInfo,
184 const nsACString& aASCIIOrigin,
185 FileManager* aFileManager,
186 mozilla::dom::ContentParent* aContentParent)
187 {
188 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
189 NS_ASSERTION(aFactory, "Null pointer!");
190 NS_ASSERTION(!aASCIIOrigin.IsEmpty(), "Empty origin!");
192 nsRefPtr<DatabaseInfo> databaseInfo(aDatabaseInfo);
193 NS_ASSERTION(databaseInfo, "Null pointer!");
195 nsRefPtr<IDBDatabase> db(new IDBDatabase(aOwnerCache));
197 db->SetScriptOwner(aOwnerCache->GetScriptOwner());
198 db->mFactory = aFactory;
199 db->mDatabaseId = databaseInfo->id;
200 db->mName = databaseInfo->name;
201 db->mFilePath = databaseInfo->filePath;
202 db->mPersistenceType = databaseInfo->persistenceType;
203 db->mGroup = databaseInfo->group;
204 databaseInfo.swap(db->mDatabaseInfo);
205 db->mASCIIOrigin = aASCIIOrigin;
206 db->mFileManager = aFileManager;
207 db->mContentParent = aContentParent;
209 QuotaManager* quotaManager = QuotaManager::Get();
210 NS_ASSERTION(quotaManager, "This should never be null!");
212 db->mQuotaClient = quotaManager->GetClient(Client::IDB);
213 NS_ASSERTION(db->mQuotaClient, "This shouldn't fail!");
215 if (!quotaManager->RegisterStorage(db)) {
216 // Either out of memory or shutting down.
217 return nullptr;
218 }
220 db->mRegistered = true;
222 return db.forget();
223 }
225 // static
226 IDBDatabase*
227 IDBDatabase::FromStorage(nsIOfflineStorage* aStorage)
228 {
229 return aStorage->GetClient()->GetType() == Client::IDB ?
230 static_cast<IDBDatabase*>(aStorage) : nullptr;
231 }
233 IDBDatabase::IDBDatabase(IDBWrapperCache* aOwnerCache)
234 : IDBWrapperCache(aOwnerCache),
235 mActorChild(nullptr),
236 mActorParent(nullptr),
237 mContentParent(nullptr),
238 mInvalidated(false),
239 mRegistered(false),
240 mClosed(false),
241 mRunningVersionChange(false)
242 {
243 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
244 }
246 IDBDatabase::~IDBDatabase()
247 {
248 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
249 }
251 void
252 IDBDatabase::LastRelease()
253 {
254 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
256 NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!");
257 if (mActorChild) {
258 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
259 mActorChild->Send__delete__(mActorChild);
260 NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!");
261 }
263 if (mRegistered) {
264 CloseInternal(true);
266 QuotaManager* quotaManager = QuotaManager::Get();
267 if (quotaManager) {
268 quotaManager->UnregisterStorage(this);
269 }
270 mRegistered = false;
271 }
272 }
274 NS_IMETHODIMP_(void)
275 IDBDatabase::Invalidate()
276 {
277 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
279 InvalidateInternal(/* aIsDead */ false);
280 }
282 void
283 IDBDatabase::InvalidateInternal(bool aIsDead)
284 {
285 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
287 if (IsInvalidated()) {
288 return;
289 }
291 mInvalidated = true;
293 // Make sure we're closed too.
294 Close();
296 // When the IndexedDatabaseManager needs to invalidate databases, all it has
297 // is an origin, so we call into the quota manager here to cancel any prompts
298 // for our owner.
299 nsPIDOMWindow* owner = GetOwner();
300 if (owner) {
301 QuotaManager::CancelPromptsForWindow(owner);
302 }
304 // We want to forcefully remove in the child when the parent has invalidated
305 // us in IPC mode because the database might no longer exist.
306 // We don't want to forcefully remove in the parent when a child dies since
307 // other child processes may be using the referenced DatabaseInfo.
308 if (!aIsDead) {
309 DatabaseInfo::Remove(mDatabaseId);
310 }
312 // And let the child process know as well.
313 if (mActorParent) {
314 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
315 mActorParent->Invalidate();
316 }
317 }
319 void
320 IDBDatabase::DisconnectFromActorParent()
321 {
322 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
323 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
325 // Make sure we're closed too.
326 Close();
328 // Kill any outstanding prompts.
329 nsPIDOMWindow* owner = GetOwner();
330 if (owner) {
331 QuotaManager::CancelPromptsForWindow(owner);
332 }
333 }
335 void
336 IDBDatabase::CloseInternal(bool aIsDead)
337 {
338 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
340 if (!mClosed) {
341 mClosed = true;
343 // If we're getting called from Unlink, avoid cloning the DatabaseInfo.
344 {
345 nsRefPtr<DatabaseInfo> previousInfo;
346 mDatabaseInfo.swap(previousInfo);
348 if (!aIsDead) {
349 mDatabaseInfo = previousInfo->Clone();
350 }
351 }
353 QuotaManager* quotaManager = QuotaManager::Get();
354 if (quotaManager) {
355 quotaManager->OnStorageClosed(this);
356 }
358 // And let the parent process know as well.
359 if (mActorChild && !IsInvalidated()) {
360 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
361 mActorChild->SendClose(aIsDead);
362 }
363 }
364 }
366 NS_IMETHODIMP_(bool)
367 IDBDatabase::IsClosed()
368 {
369 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
370 return mClosed;
371 }
373 void
374 IDBDatabase::EnterSetVersionTransaction()
375 {
376 NS_ASSERTION(!mRunningVersionChange, "How did that happen?");
378 mPreviousDatabaseInfo = mDatabaseInfo->Clone();
380 mRunningVersionChange = true;
381 }
383 void
384 IDBDatabase::ExitSetVersionTransaction()
385 {
386 NS_ASSERTION(mRunningVersionChange, "How did that happen?");
388 mPreviousDatabaseInfo = nullptr;
390 mRunningVersionChange = false;
391 }
393 void
394 IDBDatabase::RevertToPreviousState()
395 {
396 mDatabaseInfo = mPreviousDatabaseInfo;
397 mPreviousDatabaseInfo = nullptr;
398 }
400 void
401 IDBDatabase::OnUnlink()
402 {
403 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
405 // We've been unlinked, at the very least we should be able to prevent further
406 // transactions from starting and unblock any other SetVersion callers.
407 CloseInternal(true);
409 // No reason for the QuotaManager to track us any longer.
410 QuotaManager* quotaManager = QuotaManager::Get();
411 if (mRegistered && quotaManager) {
412 quotaManager->UnregisterStorage(this);
414 // Don't try to unregister again in the destructor.
415 mRegistered = false;
416 }
417 }
419 already_AddRefed<IDBObjectStore>
420 IDBDatabase::CreateObjectStoreInternal(IDBTransaction* aTransaction,
421 const ObjectStoreInfoGuts& aInfo,
422 ErrorResult& aRv)
423 {
424 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
425 NS_ASSERTION(aTransaction, "Null transaction!");
427 DatabaseInfo* databaseInfo = aTransaction->DBInfo();
429 nsRefPtr<ObjectStoreInfo> newInfo = new ObjectStoreInfo();
430 *static_cast<ObjectStoreInfoGuts*>(newInfo.get()) = aInfo;
432 newInfo->nextAutoIncrementId = aInfo.autoIncrement ? 1 : 0;
433 newInfo->comittedAutoIncrementId = newInfo->nextAutoIncrementId;
435 if (!databaseInfo->PutObjectStore(newInfo)) {
436 IDB_WARNING("Put failed!");
437 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
438 return nullptr;
439 }
441 // Don't leave this in the hash if we fail below!
442 AutoRemoveObjectStore autoRemove(databaseInfo, newInfo->name);
444 nsRefPtr<IDBObjectStore> objectStore =
445 aTransaction->GetOrCreateObjectStore(newInfo->name, newInfo, true);
446 if (!objectStore) {
447 IDB_WARNING("Failed to get objectStore!");
448 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
449 return nullptr;
450 }
452 if (IndexedDatabaseManager::IsMainProcess()) {
453 nsRefPtr<CreateObjectStoreHelper> helper =
454 new CreateObjectStoreHelper(aTransaction, objectStore);
456 nsresult rv = helper->DispatchToTransactionPool();
457 if (NS_FAILED(rv)) {
458 IDB_WARNING("Failed to dispatch!");
459 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
460 return nullptr;
461 }
462 }
464 autoRemove.forget();
466 IDB_PROFILER_MARK("IndexedDB Pseudo-request: "
467 "database(%s).transaction(%s).createObjectStore(%s)",
468 "MT IDBDatabase.createObjectStore()",
469 IDB_PROFILER_STRING(this),
470 IDB_PROFILER_STRING(aTransaction),
471 IDB_PROFILER_STRING(objectStore));
473 return objectStore.forget();
474 }
476 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBDatabase)
478 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBDatabase, IDBWrapperCache)
479 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFactory)
480 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
482 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBDatabase, IDBWrapperCache)
483 // Don't unlink mFactory!
485 // Do some cleanup.
486 tmp->OnUnlink();
487 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
489 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBDatabase)
490 NS_INTERFACE_MAP_ENTRY(nsIFileStorage)
491 NS_INTERFACE_MAP_ENTRY(nsIOfflineStorage)
492 NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache)
494 NS_IMPL_ADDREF_INHERITED(IDBDatabase, IDBWrapperCache)
495 NS_IMPL_RELEASE_INHERITED(IDBDatabase, IDBWrapperCache)
497 JSObject*
498 IDBDatabase::WrapObject(JSContext* aCx)
499 {
500 return IDBDatabaseBinding::Wrap(aCx, this);
501 }
503 uint64_t
504 IDBDatabase::Version() const
505 {
506 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
507 DatabaseInfo* info = Info();
508 return info->version;
509 }
511 already_AddRefed<DOMStringList>
512 IDBDatabase::GetObjectStoreNames(ErrorResult& aRv) const
513 {
514 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
516 DatabaseInfo* info = Info();
518 nsRefPtr<DOMStringList> list(new DOMStringList());
519 if (!info->GetObjectStoreNames(list->StringArray())) {
520 IDB_WARNING("Couldn't get names!");
521 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
522 return nullptr;
523 }
525 return list.forget();
526 }
528 already_AddRefed<IDBObjectStore>
529 IDBDatabase::CreateObjectStore(
530 JSContext* aCx, const nsAString& aName,
531 const IDBObjectStoreParameters& aOptionalParameters,
532 ErrorResult& aRv)
533 {
534 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
536 IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction();
538 if (!transaction ||
539 transaction->GetMode() != IDBTransaction::VERSION_CHANGE) {
540 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
541 return nullptr;
542 }
544 DatabaseInfo* databaseInfo = transaction->DBInfo();
546 KeyPath keyPath(0);
547 if (NS_FAILED(KeyPath::Parse(aCx, aOptionalParameters.mKeyPath, &keyPath))) {
548 aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
549 return nullptr;
550 }
552 if (databaseInfo->ContainsStoreName(aName)) {
553 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR);
554 return nullptr;
555 }
557 if (!keyPath.IsAllowedForObjectStore(aOptionalParameters.mAutoIncrement)) {
558 aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
559 return nullptr;
560 }
562 ObjectStoreInfoGuts guts;
564 guts.name = aName;
565 guts.id = databaseInfo->nextObjectStoreId++;
566 guts.keyPath = keyPath;
567 guts.autoIncrement = aOptionalParameters.mAutoIncrement;
569 return CreateObjectStoreInternal(transaction, guts, aRv);
570 }
572 void
573 IDBDatabase::DeleteObjectStore(const nsAString& aName, ErrorResult& aRv)
574 {
575 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
577 IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction();
579 if (!transaction ||
580 transaction->GetMode() != IDBTransaction::VERSION_CHANGE) {
581 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
582 return;
583 }
585 DatabaseInfo* info = transaction->DBInfo();
586 ObjectStoreInfo* objectStoreInfo = info->GetObjectStore(aName);
587 if (!objectStoreInfo) {
588 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR);
589 return;
590 }
592 if (IndexedDatabaseManager::IsMainProcess()) {
593 nsRefPtr<DeleteObjectStoreHelper> helper =
594 new DeleteObjectStoreHelper(transaction, objectStoreInfo->id);
596 nsresult rv = helper->DispatchToTransactionPool();
597 if (NS_FAILED(rv)) {
598 IDB_WARNING("Failed to dispatch!");
599 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
600 return;
601 }
602 }
603 else {
604 IndexedDBTransactionChild* actor = transaction->GetActorChild();
605 NS_ASSERTION(actor, "Must have an actor here!");
607 actor->SendDeleteObjectStore(nsString(aName));
608 }
610 transaction->RemoveObjectStore(aName);
612 IDB_PROFILER_MARK("IndexedDB Pseudo-request: "
613 "database(%s).transaction(%s).deleteObjectStore(\"%s\")",
614 "MT IDBDatabase.deleteObjectStore()",
615 IDB_PROFILER_STRING(this),
616 IDB_PROFILER_STRING(transaction),
617 NS_ConvertUTF16toUTF8(aName).get());
618 }
620 already_AddRefed<indexedDB::IDBTransaction>
621 IDBDatabase::Transaction(const Sequence<nsString>& aStoreNames,
622 IDBTransactionMode aMode, ErrorResult& aRv)
623 {
624 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
626 if (QuotaManager::IsShuttingDown()) {
627 IDB_REPORT_INTERNAL_ERR();
628 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
629 return nullptr;
630 }
632 if (mClosed) {
633 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
634 return nullptr;
635 }
637 if (mRunningVersionChange) {
638 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
639 return nullptr;
640 }
642 if (aStoreNames.IsEmpty()) {
643 aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
644 return nullptr;
645 }
647 IDBTransaction::Mode transactionMode = IDBTransaction::READ_ONLY;
648 switch (aMode) {
649 case IDBTransactionMode::Readonly:
650 transactionMode = IDBTransaction::READ_ONLY;
651 break;
652 case IDBTransactionMode::Readwrite:
653 transactionMode = IDBTransaction::READ_WRITE;
654 break;
655 case IDBTransactionMode::Versionchange:
656 transactionMode = IDBTransaction::VERSION_CHANGE;
657 break;
658 default:
659 MOZ_CRASH("Unknown mode!");
660 }
662 // Now check to make sure the object store names we collected actually exist.
663 DatabaseInfo* info = Info();
664 for (uint32_t index = 0; index < aStoreNames.Length(); index++) {
665 if (!info->ContainsStoreName(aStoreNames[index])) {
666 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR);
667 return nullptr;
668 }
669 }
671 nsRefPtr<IDBTransaction> transaction =
672 IDBTransaction::Create(this, aStoreNames, transactionMode, false);
673 if (!transaction) {
674 IDB_WARNING("Failed to create the transaction!");
675 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
676 return nullptr;
677 }
679 IDB_PROFILER_MARK("IndexedDB Transaction %llu: database(%s).transaction(%s)",
680 "IDBTransaction[%llu] MT Started",
681 transaction->GetSerialNumber(), IDB_PROFILER_STRING(this),
682 IDB_PROFILER_STRING(transaction));
684 return transaction.forget();
685 }
687 already_AddRefed<IDBRequest>
688 IDBDatabase::MozCreateFileHandle(const nsAString& aName,
689 const Optional<nsAString>& aType,
690 ErrorResult& aRv)
691 {
692 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
694 if (!IndexedDatabaseManager::IsMainProcess()) {
695 IDB_WARNING("Not supported yet!");
696 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
697 return nullptr;
698 }
700 if (QuotaManager::IsShuttingDown()) {
701 IDB_REPORT_INTERNAL_ERR();
702 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
703 return nullptr;
704 }
706 if (mClosed) {
707 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
708 return nullptr;
709 }
711 nsRefPtr<IDBRequest> request = IDBRequest::Create(this, nullptr);
713 nsRefPtr<CreateFileHelper> helper =
714 new CreateFileHelper(this, request, aName,
715 aType.WasPassed() ? aType.Value() : EmptyString());
717 QuotaManager* quotaManager = QuotaManager::Get();
718 NS_ASSERTION(quotaManager, "We should definitely have a manager here");
720 nsresult rv = helper->Dispatch(quotaManager->IOThread());
721 if (NS_FAILED(rv)) {
722 IDB_WARNING("Failed to dispatch!");
723 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
724 return nullptr;
725 }
727 return request.forget();
728 }
730 NS_IMETHODIMP
731 IDBDatabase::Close()
732 {
733 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
735 CloseInternal(false);
737 NS_ASSERTION(mClosed, "Should have set the closed flag!");
739 return NS_OK;
740 }
742 NS_IMETHODIMP_(const nsACString&)
743 IDBDatabase::Id()
744 {
745 return mDatabaseId;
746 }
748 NS_IMETHODIMP_(bool)
749 IDBDatabase::IsInvalidated()
750 {
751 return mInvalidated;
752 }
754 NS_IMETHODIMP_(bool)
755 IDBDatabase::IsShuttingDown()
756 {
757 return QuotaManager::IsShuttingDown();
758 }
760 NS_IMETHODIMP_(void)
761 IDBDatabase::SetThreadLocals()
762 {
763 NS_ASSERTION(GetOwner(), "Should have owner!");
764 QuotaManager::SetCurrentWindow(GetOwner());
765 }
767 NS_IMETHODIMP_(void)
768 IDBDatabase::UnsetThreadLocals()
769 {
770 QuotaManager::SetCurrentWindow(nullptr);
771 }
773 NS_IMETHODIMP_(mozilla::dom::quota::Client*)
774 IDBDatabase::GetClient()
775 {
776 return mQuotaClient;
777 }
779 NS_IMETHODIMP_(bool)
780 IDBDatabase::IsOwned(nsPIDOMWindow* aOwner)
781 {
782 return GetOwner() == aOwner;
783 }
785 NS_IMETHODIMP_(const nsACString&)
786 IDBDatabase::Origin()
787 {
788 return mASCIIOrigin;
789 }
791 nsresult
792 IDBDatabase::PostHandleEvent(EventChainPostVisitor& aVisitor)
793 {
794 return IndexedDatabaseManager::FireWindowOnError(GetOwner(), aVisitor);
795 }
797 AsyncConnectionHelper::ChildProcessSendResult
798 NoRequestDatabaseHelper::SendResponseToChildProcess(nsresult aResultCode)
799 {
800 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
801 return Success_NotSent;
802 }
804 nsresult
805 NoRequestDatabaseHelper::UnpackResponseFromParentProcess(
806 const ResponseValue& aResponseValue)
807 {
808 MOZ_CRASH("Should never get here!");
809 }
811 nsresult
812 NoRequestDatabaseHelper::OnSuccess()
813 {
814 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
815 return NS_OK;
816 }
818 void
819 NoRequestDatabaseHelper::OnError()
820 {
821 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
822 mTransaction->Abort(GetResultCode());
823 }
825 nsresult
826 CreateObjectStoreHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
827 {
828 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
829 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
831 PROFILER_LABEL("IndexedDB", "CreateObjectStoreHelper::DoDatabaseWork");
833 if (IndexedDatabaseManager::InLowDiskSpaceMode()) {
834 NS_WARNING("Refusing to create additional objectStore because disk space "
835 "is low!");
836 return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
837 }
839 nsCOMPtr<mozIStorageStatement> stmt =
840 mTransaction->GetCachedStatement(NS_LITERAL_CSTRING(
841 "INSERT INTO object_store (id, auto_increment, name, key_path) "
842 "VALUES (:id, :auto_increment, :name, :key_path)"
843 ));
844 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
846 mozStorageStatementScoper scoper(stmt);
848 nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"),
849 mObjectStore->Id());
850 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
852 rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("auto_increment"),
853 mObjectStore->IsAutoIncrement() ? 1 : 0);
854 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
856 rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mObjectStore->Name());
857 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
859 const KeyPath& keyPath = mObjectStore->GetKeyPath();
860 if (keyPath.IsValid()) {
861 nsAutoString keyPathSerialization;
862 keyPath.SerializeToString(keyPathSerialization);
863 rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key_path"),
864 keyPathSerialization);
865 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
866 }
867 else {
868 rv = stmt->BindNullByName(NS_LITERAL_CSTRING("key_path"));
869 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
870 }
872 rv = stmt->Execute();
873 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
875 return NS_OK;
876 }
878 void
879 CreateObjectStoreHelper::ReleaseMainThreadObjects()
880 {
881 mObjectStore = nullptr;
882 NoRequestDatabaseHelper::ReleaseMainThreadObjects();
883 }
885 nsresult
886 DeleteObjectStoreHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
887 {
888 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
889 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
891 PROFILER_LABEL("IndexedDB", "DeleteObjectStoreHelper::DoDatabaseWork");
893 nsCOMPtr<mozIStorageStatement> stmt =
894 mTransaction->GetCachedStatement(NS_LITERAL_CSTRING(
895 "DELETE FROM object_store "
896 "WHERE id = :id "
897 ));
898 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
900 mozStorageStatementScoper scoper(stmt);
902 nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mObjectStoreId);
903 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
905 rv = stmt->Execute();
906 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
908 return NS_OK;
909 }
911 nsresult
912 CreateFileHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
913 {
914 AssertIsOnIOThread();
915 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
917 PROFILER_LABEL("IndexedDB", "CreateFileHelper::DoDatabaseWork");
919 if (IndexedDatabaseManager::InLowDiskSpaceMode()) {
920 NS_WARNING("Refusing to create file because disk space is low!");
921 return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
922 }
924 FileManager* fileManager = mDatabase->Manager();
926 mFileInfo = fileManager->GetNewFileInfo();
927 IDB_ENSURE_TRUE(mFileInfo, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
929 const int64_t& fileId = mFileInfo->Id();
931 nsCOMPtr<nsIFile> directory = fileManager->EnsureJournalDirectory();
932 NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE);
934 nsCOMPtr<nsIFile> file = fileManager->GetFileForId(directory, fileId);
935 NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
937 nsresult rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
938 NS_ENSURE_SUCCESS(rv, rv);
940 directory = fileManager->GetDirectory();
941 IDB_ENSURE_TRUE(directory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
943 file = fileManager->GetFileForId(directory, fileId);
944 IDB_ENSURE_TRUE(file, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
946 rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
947 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
949 return NS_OK;
950 }
952 nsresult
953 CreateFileHelper::GetSuccessResult(JSContext* aCx,
954 JS::MutableHandle<JS::Value> aVal)
955 {
956 nsRefPtr<IDBFileHandle> fileHandle =
957 IDBFileHandle::Create(mDatabase, mName, mType, mFileInfo.forget());
958 IDB_ENSURE_TRUE(fileHandle, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
960 return WrapNative(aCx, NS_ISUPPORTS_CAST(EventTarget*, fileHandle), aVal);
961 }