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 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is Indexed Database.
18 * The Initial Developer of the Original Code is
19 * The Mozilla Foundation.
20 * Portions created by the Initial Developer are Copyright (C) 2010
21 * the Initial Developer. All Rights Reserved.
24 * Ben Turner <bent.mozilla@gmail.com>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "IDBDatabase.h"
43 #include "mozilla/Mutex.h"
44 #include "mozilla/storage.h"
45 #include "nsDOMClassInfo.h"
46 #include "nsEventDispatcher.h"
47 #include "nsJSUtils.h"
48 #include "nsProxyRelease.h"
49 #include "nsThreadUtils.h"
51 #include "AsyncConnectionHelper.h"
52 #include "CheckQuotaHelper.h"
53 #include "DatabaseInfo.h"
54 #include "IDBEvents.h"
56 #include "IDBObjectStore.h"
57 #include "IDBTransaction.h"
58 #include "IDBFactory.h"
59 #include "IndexedDatabaseManager.h"
60 #include "LazyIdleThread.h"
61 #include "TransactionThreadPool.h"
63 USING_INDEXEDDB_NAMESPACE
67 const PRUint32 kDefaultDatabaseTimeoutSeconds
= 30;
69 PRUint32 gDatabaseInstanceCount
= 0;
70 mozilla::Mutex
* gPromptHelpersMutex
= nsnull
;
72 // Protected by gPromptHelpersMutex.
73 nsTArray
<nsRefPtr
<CheckQuotaHelper
> >* gPromptHelpers
= nsnull
;
75 class SetVersionHelper
: public AsyncConnectionHelper
78 SetVersionHelper(IDBTransaction
* aTransaction
,
80 const nsAString
& aVersion
)
81 : AsyncConnectionHelper(aTransaction
, aRequest
), mVersion(aVersion
)
84 nsresult
DoDatabaseWork(mozIStorageConnection
* aConnection
);
85 nsresult
GetSuccessResult(JSContext
* aCx
,
93 class CreateObjectStoreHelper
: public AsyncConnectionHelper
96 CreateObjectStoreHelper(IDBTransaction
* aTransaction
,
97 IDBObjectStore
* aObjectStore
)
98 : AsyncConnectionHelper(aTransaction
, nsnull
), mObjectStore(aObjectStore
)
101 nsresult
DoDatabaseWork(mozIStorageConnection
* aConnection
);
110 NS_ASSERTION(mTransaction
->IsAborted(), "How else can this fail?!");
113 void ReleaseMainThreadObjects()
115 mObjectStore
= nsnull
;
116 AsyncConnectionHelper::ReleaseMainThreadObjects();
120 nsRefPtr
<IDBObjectStore
> mObjectStore
;
123 class DeleteObjectStoreHelper
: public AsyncConnectionHelper
126 DeleteObjectStoreHelper(IDBTransaction
* aTransaction
,
127 PRInt64 aObjectStoreId
)
128 : AsyncConnectionHelper(aTransaction
, nsnull
), mObjectStoreId(aObjectStoreId
)
131 nsresult
DoDatabaseWork(mozIStorageConnection
* aConnection
);
140 NS_ASSERTION(mTransaction
->IsAborted(), "How else can this fail?!");
145 PRInt64 mObjectStoreId
;
152 AutoFree(void* aPtr
) : mPtr(aPtr
) { }
153 ~AutoFree() { NS_Free(mPtr
); }
159 class AutoRemoveObjectStore
162 AutoRemoveObjectStore(PRUint32 aId
, const nsAString
& aName
)
163 : mId(aId
), mName(aName
)
166 ~AutoRemoveObjectStore()
169 ObjectStoreInfo::Remove(mId
, mName
);
185 ConvertVariantToStringArray(nsIVariant
* aVariant
,
186 nsTArray
<nsString
>& aStringArray
)
190 NS_ASSERTION(NS_SUCCEEDED(aVariant
->GetDataType(&type
)) &&
191 type
== nsIDataType::VTYPE_ARRAY
, "Bad arg!");
199 nsresult rv
= aVariant
->GetAsArray(&valueType
, &iid
, &valueCount
, &rawArray
);
200 NS_ENSURE_SUCCESS(rv
, rv
);
202 AutoFree
af(rawArray
);
204 // Just delete anything that we don't expect and return.
205 if (valueType
!= nsIDataType::VTYPE_WCHAR_STR
) {
207 case nsIDataType::VTYPE_ID
:
208 case nsIDataType::VTYPE_CHAR_STR
: {
209 char** charArray
= reinterpret_cast<char**>(rawArray
);
210 for (PRUint32 index
= 0; index
< valueCount
; index
++) {
211 if (charArray
[index
]) {
212 NS_Free(charArray
[index
]);
217 case nsIDataType::VTYPE_INTERFACE
:
218 case nsIDataType::VTYPE_INTERFACE_IS
: {
219 nsISupports
** supportsArray
= reinterpret_cast<nsISupports
**>(rawArray
);
220 for (PRUint32 index
= 0; index
< valueCount
; index
++) {
221 NS_IF_RELEASE(supportsArray
[index
]);
226 // The other types are primitives that do not need to be freed.
230 return NS_ERROR_ILLEGAL_VALUE
;
233 PRUnichar
** strings
= reinterpret_cast<PRUnichar
**>(rawArray
);
235 for (PRUint32 index
= 0; index
< valueCount
; index
++) {
236 nsString
* newString
= aStringArray
.AppendElement();
239 NS_ERROR("Out of memory?");
240 for (; index
< valueCount
; index
++) {
241 NS_Free(strings
[index
]);
243 return NS_ERROR_OUT_OF_MEMORY
;
246 newString
->Adopt(strings
[index
], -1);
252 } // anonymous namespace
255 already_AddRefed
<IDBDatabase
>
256 IDBDatabase::Create(nsIScriptContext
* aScriptContext
,
257 nsPIDOMWindow
* aOwner
,
258 DatabaseInfo
* aDatabaseInfo
,
259 const nsACString
& aASCIIOrigin
)
261 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
262 NS_ASSERTION(aDatabaseInfo
, "Null pointer!");
263 NS_ASSERTION(!aASCIIOrigin
.IsEmpty(), "Empty origin!");
265 nsRefPtr
<IDBDatabase
> db(new IDBDatabase());
267 db
->mScriptContext
= aScriptContext
;
270 db
->mDatabaseId
= aDatabaseInfo
->id
;
271 db
->mName
= aDatabaseInfo
->name
;
272 db
->mFilePath
= aDatabaseInfo
->filePath
;
273 db
->mASCIIOrigin
= aASCIIOrigin
;
275 IndexedDatabaseManager
* mgr
= IndexedDatabaseManager::Get();
276 NS_ASSERTION(mgr
, "This should never be null!");
278 if (!mgr
->RegisterDatabase(db
)) {
279 // Either out of memory or shutting down.
286 IDBDatabase::IDBDatabase()
292 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
294 if (!gDatabaseInstanceCount
++) {
295 NS_ASSERTION(!gPromptHelpersMutex
, "Should be null!");
296 gPromptHelpersMutex
= new mozilla::Mutex("IDBDatabase gPromptHelpersMutex");
300 IDBDatabase::~IDBDatabase()
302 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
307 IndexedDatabaseManager
* mgr
= IndexedDatabaseManager::Get();
309 mgr
->UnregisterDatabase(this);
313 if (mDatabaseId
&& !mInvalidated
) {
315 if (!DatabaseInfo::Get(mDatabaseId
, &info
)) {
316 NS_ERROR("This should never fail!");
319 NS_ASSERTION(info
->referenceCount
, "Bad reference count!");
320 if (--info
->referenceCount
== 0) {
321 DatabaseInfo::Remove(mDatabaseId
);
325 if (mListenerManager
) {
326 mListenerManager
->Disconnect();
329 if (!--gDatabaseInstanceCount
) {
330 NS_ASSERTION(gPromptHelpersMutex
, "Should not be null!");
332 delete gPromptHelpers
;
333 gPromptHelpers
= nsnull
;
335 delete gPromptHelpersMutex
;
336 gPromptHelpersMutex
= nsnull
;
341 IDBDatabase::IsQuotaDisabled()
343 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
344 NS_ASSERTION(gPromptHelpersMutex
, "This should never be null!");
346 MutexAutoLock
lock(*gPromptHelpersMutex
);
348 if (!gPromptHelpers
) {
349 gPromptHelpers
= new nsAutoTArray
<nsRefPtr
<CheckQuotaHelper
>, 10>();
352 CheckQuotaHelper
* foundHelper
= nsnull
;
354 PRUint32 count
= gPromptHelpers
->Length();
355 for (PRUint32 index
= 0; index
< count
; index
++) {
356 nsRefPtr
<CheckQuotaHelper
>& helper
= gPromptHelpers
->ElementAt(index
);
357 if (helper
->WindowSerial() == Owner()->GetSerial()) {
358 foundHelper
= helper
;
364 nsRefPtr
<CheckQuotaHelper
>* newHelper
= gPromptHelpers
->AppendElement();
366 NS_WARNING("Out of memory!");
369 *newHelper
= new CheckQuotaHelper(this, *gPromptHelpersMutex
);
370 foundHelper
= *newHelper
;
373 // Unlock before calling out to XPCOM.
374 MutexAutoUnlock
unlock(*gPromptHelpersMutex
);
376 nsresult rv
= NS_DispatchToMainThread(foundHelper
, NS_DISPATCH_NORMAL
);
377 NS_ENSURE_SUCCESS(rv
, false);
381 return foundHelper
->PromptAndReturnQuotaIsDisabled();
385 IDBDatabase::Invalidate()
387 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
388 NS_ASSERTION(gPromptHelpersMutex
, "This should never be null!");
390 // Make sure we're closed too.
393 // Cancel any quota prompts that are currently being displayed.
395 MutexAutoLock
lock(*gPromptHelpersMutex
);
397 if (gPromptHelpers
) {
398 PRUint32 count
= gPromptHelpers
->Length();
399 for (PRUint32 index
= 0; index
< count
; index
++) {
400 nsRefPtr
<CheckQuotaHelper
>& helper
= gPromptHelpers
->ElementAt(index
);
401 if (helper
->WindowSerial() == Owner()->GetSerial()) {
409 if (!PR_AtomicSet(&mInvalidated
, 1)) {
411 if (!DatabaseInfo::Get(mDatabaseId
, &info
)) {
412 NS_ERROR("This should never fail!");
415 NS_ASSERTION(info
->referenceCount
, "Bad reference count!");
416 if (--info
->referenceCount
== 0) {
417 DatabaseInfo::Remove(mDatabaseId
);
423 IDBDatabase::IsInvalidated()
425 return !!mInvalidated
;
429 IDBDatabase::CloseInternal()
431 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
434 IndexedDatabaseManager
* mgr
= IndexedDatabaseManager::Get();
436 mgr
->OnDatabaseClosed(this);
443 IDBDatabase::IsClosed()
445 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
450 IDBDatabase::OnUnlink()
452 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
453 NS_ASSERTION(!mOwner
, "Should have been cleared already!");
455 // We've been unlinked, at the very least we should be able to prevent further
456 // transactions from starting and unblock any other SetVersion callers.
459 // No reason for the IndexedDatabaseManager to track us any longer.
460 IndexedDatabaseManager
* mgr
= IndexedDatabaseManager::Get();
462 mgr
->UnregisterDatabase(this);
464 // Don't try to unregister again in the destructor.
469 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBDatabase
)
471 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBDatabase
,
472 nsDOMEventTargetHelper
)
473 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnErrorListener
)
474 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnVersionChangeListener
)
475 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
477 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBDatabase
,
478 nsDOMEventTargetHelper
)
479 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnErrorListener
)
480 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnVersionChangeListener
)
484 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
486 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBDatabase
)
487 NS_INTERFACE_MAP_ENTRY(nsIIDBDatabase
)
488 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBDatabase
)
489 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper
)
491 NS_IMPL_ADDREF_INHERITED(IDBDatabase
, nsDOMEventTargetHelper
)
492 NS_IMPL_RELEASE_INHERITED(IDBDatabase
, nsDOMEventTargetHelper
)
494 DOMCI_DATA(IDBDatabase
, IDBDatabase
)
497 IDBDatabase::GetName(nsAString
& aName
)
499 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
505 IDBDatabase::GetVersion(nsAString
& aVersion
)
507 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
509 if (!DatabaseInfo::Get(mDatabaseId
, &info
)) {
510 NS_ERROR("This should never fail!");
512 aVersion
.Assign(info
->version
);
517 IDBDatabase::GetObjectStoreNames(nsIDOMDOMStringList
** aObjectStores
)
519 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
522 if (!DatabaseInfo::Get(mDatabaseId
, &info
)) {
523 NS_ERROR("This should never fail!");
526 nsAutoTArray
<nsString
, 10> objectStoreNames
;
527 if (!info
->GetObjectStoreNames(objectStoreNames
)) {
528 NS_WARNING("Couldn't get names!");
529 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
532 nsRefPtr
<nsDOMStringList
> list(new nsDOMStringList());
533 PRUint32 count
= objectStoreNames
.Length();
534 for (PRUint32 index
= 0; index
< count
; index
++) {
535 NS_ENSURE_TRUE(list
->Add(objectStoreNames
[index
]),
536 NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
539 list
.forget(aObjectStores
);
544 IDBDatabase::CreateObjectStore(const nsAString
& aName
,
545 const jsval
& aOptions
,
547 nsIIDBObjectStore
** _retval
)
549 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
551 if (aName
.IsEmpty()) {
552 // XXX Update spec for a real error code here.
553 return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR
;
556 IDBTransaction
* transaction
= AsyncConnectionHelper::GetCurrentTransaction();
559 transaction
->Mode() != nsIIDBTransaction::VERSION_CHANGE
) {
560 return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR
;
563 DatabaseInfo
* databaseInfo
;
564 if (!DatabaseInfo::Get(mDatabaseId
, &databaseInfo
)) {
565 NS_ERROR("This should never fail!");
568 if (databaseInfo
->ContainsStoreName(aName
)) {
569 return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR
;
573 bool autoIncrement
= false;
575 if (!JSVAL_IS_VOID(aOptions
) && !JSVAL_IS_NULL(aOptions
)) {
576 if (JSVAL_IS_PRIMITIVE(aOptions
)) {
577 // XXX Update spec for a real code here
578 return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR
;
581 NS_ASSERTION(JSVAL_IS_OBJECT(aOptions
), "Huh?!");
582 JSObject
* options
= JSVAL_TO_OBJECT(aOptions
);
584 js::AutoIdArray
ids(aCx
, JS_Enumerate(aCx
, options
));
586 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
589 for (size_t index
= 0; index
< ids
.length(); index
++) {
590 jsid id
= ids
[index
];
592 if (id
!= nsDOMClassInfo::sKeyPath_id
&&
593 id
!= nsDOMClassInfo::sAutoIncrement_id
) {
594 // XXX Update spec for a real code here
595 return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR
;
599 if (!JS_GetPropertyById(aCx
, options
, id
, &val
)) {
600 NS_WARNING("JS_GetPropertyById failed!");
601 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
604 if (id
== nsDOMClassInfo::sKeyPath_id
) {
605 JSString
* str
= JS_ValueToString(aCx
, val
);
607 NS_WARNING("JS_ValueToString failed!");
608 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
610 nsDependentJSString dependentKeyPath
;
611 if (!dependentKeyPath
.init(aCx
, str
)) {
612 NS_WARNING("Initializing keyPath failed!");
613 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
615 keyPath
= dependentKeyPath
;
617 else if (id
== nsDOMClassInfo::sAutoIncrement_id
) {
619 if (!JS_ValueToBoolean(aCx
, val
, &boolVal
)) {
620 NS_WARNING("JS_ValueToBoolean failed!");
621 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
623 autoIncrement
= !!boolVal
;
626 NS_NOTREACHED("Shouldn't be able to get here!");
631 nsAutoPtr
<ObjectStoreInfo
> newInfo(new ObjectStoreInfo());
633 newInfo
->name
= aName
;
634 newInfo
->id
= databaseInfo
->nextObjectStoreId
++;
635 newInfo
->keyPath
= keyPath
;
636 newInfo
->autoIncrement
= autoIncrement
;
637 newInfo
->databaseId
= mDatabaseId
;
639 if (!ObjectStoreInfo::Put(newInfo
)) {
640 NS_WARNING("Put failed!");
641 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
643 ObjectStoreInfo
* objectStoreInfo
= newInfo
.forget();
645 // Don't leave this in the hash if we fail below!
646 AutoRemoveObjectStore
autoRemove(mDatabaseId
, aName
);
648 nsRefPtr
<IDBObjectStore
> objectStore
=
649 transaction
->GetOrCreateObjectStore(aName
, objectStoreInfo
);
650 NS_ENSURE_TRUE(objectStore
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
652 nsRefPtr
<CreateObjectStoreHelper
> helper
=
653 new CreateObjectStoreHelper(transaction
, objectStore
);
655 nsresult rv
= helper
->DispatchToTransactionPool();
656 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
660 objectStore
.forget(_retval
);
665 IDBDatabase::DeleteObjectStore(const nsAString
& aName
)
667 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
669 IDBTransaction
* transaction
= AsyncConnectionHelper::GetCurrentTransaction();
672 transaction
->Mode() != nsIIDBTransaction::VERSION_CHANGE
) {
673 return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR
;
676 ObjectStoreInfo
* objectStoreInfo
;
677 if (!ObjectStoreInfo::Get(mDatabaseId
, aName
, &objectStoreInfo
)) {
678 return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR
;
681 nsRefPtr
<DeleteObjectStoreHelper
> helper
=
682 new DeleteObjectStoreHelper(transaction
, objectStoreInfo
->id
);
683 nsresult rv
= helper
->DispatchToTransactionPool();
684 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
686 ObjectStoreInfo::Remove(mDatabaseId
, aName
);
691 IDBDatabase::SetVersion(const nsAString
& aVersion
,
693 nsIIDBRequest
** _retval
)
695 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
698 // XXX Update spec for a real error code here.
699 return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR
;
703 if (!DatabaseInfo::Get(mDatabaseId
, &info
)) {
704 NS_ERROR("This should never fail!");
707 // Lock the whole database.
708 nsTArray
<nsString
> storesToOpen
;
709 nsRefPtr
<IDBTransaction
> transaction
=
710 IDBTransaction::Create(this, storesToOpen
, IDBTransaction::VERSION_CHANGE
,
711 kDefaultDatabaseTimeoutSeconds
, true);
712 NS_ENSURE_TRUE(transaction
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
714 nsRefPtr
<IDBVersionChangeRequest
> request
=
715 IDBVersionChangeRequest::Create(static_cast<nsPIDOMEventTarget
*>(this),
716 ScriptContext(), Owner(), transaction
);
717 NS_ENSURE_TRUE(request
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
719 nsRefPtr
<SetVersionHelper
> helper
=
720 new SetVersionHelper(transaction
, request
, aVersion
);
722 IndexedDatabaseManager
* mgr
= IndexedDatabaseManager::Get();
723 NS_ASSERTION(mgr
, "This should never be null!");
725 nsresult rv
= mgr
->SetDatabaseVersion(this, request
, aVersion
, helper
);
726 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
728 request
.forget(_retval
);
733 IDBDatabase::Transaction(nsIVariant
* aStoreNames
,
737 PRUint8 aOptionalArgCount
,
738 nsIIDBTransaction
** _retval
)
740 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
742 if (IndexedDatabaseManager::IsShuttingDown()) {
743 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
747 return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR
;
750 if (aOptionalArgCount
) {
751 if (aMode
!= nsIIDBTransaction::READ_WRITE
&&
752 aMode
!= nsIIDBTransaction::READ_ONLY
) {
753 return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR
;
757 aMode
= nsIIDBTransaction::READ_ONLY
;
760 if (aOptionalArgCount
<= 1) {
761 aTimeout
= kDefaultDatabaseTimeoutSeconds
;
765 nsresult rv
= aStoreNames
->GetDataType(&type
);
766 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
769 if (!DatabaseInfo::Get(mDatabaseId
, &info
)) {
770 NS_ERROR("This should never fail!");
773 nsTArray
<nsString
> storesToOpen
;
776 case nsIDataType::VTYPE_VOID
:
777 case nsIDataType::VTYPE_EMPTY
:
778 case nsIDataType::VTYPE_EMPTY_ARRAY
: {
779 // Empty, request all object stores
780 if (!info
->GetObjectStoreNames(storesToOpen
)) {
781 NS_WARNING("Out of memory?");
782 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
786 case nsIDataType::VTYPE_WSTRING_SIZE_IS
: {
789 rv
= aStoreNames
->GetAsAString(name
);
790 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
792 if (!info
->ContainsStoreName(name
)) {
793 return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR
;
796 if (!storesToOpen
.AppendElement(name
)) {
797 NS_WARNING("Out of memory?");
798 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
802 case nsIDataType::VTYPE_ARRAY
: {
803 nsTArray
<nsString
> names
;
804 rv
= ConvertVariantToStringArray(aStoreNames
, names
);
805 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
807 PRUint32 nameCount
= names
.Length();
808 for (PRUint32 nameIndex
= 0; nameIndex
< nameCount
; nameIndex
++) {
809 nsString
& name
= names
[nameIndex
];
811 if (!info
->ContainsStoreName(name
)) {
812 return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR
;
815 if (!storesToOpen
.AppendElement(name
)) {
816 NS_WARNING("Out of memory?");
817 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
820 NS_ASSERTION(nameCount
== storesToOpen
.Length(), "Should have bailed!");
823 case nsIDataType::VTYPE_INTERFACE
:
824 case nsIDataType::VTYPE_INTERFACE_IS
: {
825 nsCOMPtr
<nsISupports
> supports
;
827 rv
= aStoreNames
->GetAsInterface(&iid
, getter_AddRefs(supports
));
828 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
832 nsCOMPtr
<nsIDOMDOMStringList
> stringList(do_QueryInterface(supports
));
834 // We don't support anything other than nsIDOMDOMStringList.
835 return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR
;
838 PRUint32 stringCount
;
839 rv
= stringList
->GetLength(&stringCount
);
840 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
842 for (PRUint32 stringIndex
= 0; stringIndex
< stringCount
; stringIndex
++) {
844 rv
= stringList
->Item(stringIndex
, name
);
845 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
847 if (!info
->ContainsStoreName(name
)) {
848 return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR
;
851 if (!storesToOpen
.AppendElement(name
)) {
852 NS_WARNING("Out of memory?");
853 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
859 return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR
;
862 nsRefPtr
<IDBTransaction
> transaction
=
863 IDBTransaction::Create(this, storesToOpen
, aMode
,
864 kDefaultDatabaseTimeoutSeconds
);
865 NS_ENSURE_TRUE(transaction
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
867 transaction
.forget(_retval
);
874 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
878 NS_ASSERTION(mClosed
, "Should have set the closed flag!");
883 IDBDatabase::SetOnerror(nsIDOMEventListener
* aErrorListener
)
885 return RemoveAddEventListener(NS_LITERAL_STRING(ERROR_EVT_STR
),
886 mOnErrorListener
, aErrorListener
);
890 IDBDatabase::GetOnerror(nsIDOMEventListener
** aErrorListener
)
892 return GetInnerEventListener(mOnErrorListener
, aErrorListener
);
896 IDBDatabase::SetOnversionchange(nsIDOMEventListener
* aVersionChangeListener
)
898 return RemoveAddEventListener(NS_LITERAL_STRING(VERSIONCHANGE_EVT_STR
),
899 mOnVersionChangeListener
,
900 aVersionChangeListener
);
904 IDBDatabase::GetOnversionchange(nsIDOMEventListener
** aVersionChangeListener
)
906 return GetInnerEventListener(mOnVersionChangeListener
,
907 aVersionChangeListener
);
911 IDBDatabase::PostHandleEvent(nsEventChainPostVisitor
& aVisitor
)
913 NS_ENSURE_TRUE(aVisitor
.mDOMEvent
, NS_ERROR_UNEXPECTED
);
915 if (aVisitor
.mEventStatus
!= nsEventStatus_eConsumeNoDefault
) {
917 nsresult rv
= aVisitor
.mDOMEvent
->GetType(type
);
918 NS_ENSURE_SUCCESS(rv
, rv
);
920 if (type
.EqualsLiteral(ERROR_EVT_STR
)) {
921 nsRefPtr
<nsDOMEvent
> duplicateEvent
= CreateGenericEvent(type
);
922 NS_ENSURE_STATE(duplicateEvent
);
924 nsCOMPtr
<nsIDOMEventTarget
> target(do_QueryInterface(mOwner
));
925 NS_ASSERTION(target
, "How can this happen?!");
928 rv
= target
->DispatchEvent(duplicateEvent
, &dummy
);
929 NS_ENSURE_SUCCESS(rv
, rv
);
937 SetVersionHelper::DoDatabaseWork(mozIStorageConnection
* aConnection
)
939 NS_PRECONDITION(aConnection
, "Passing a null connection!");
941 nsCOMPtr
<mozIStorageStatement
> stmt
;
942 nsresult rv
= aConnection
->CreateStatement(NS_LITERAL_CSTRING(
944 "SET version = :version"
945 ), getter_AddRefs(stmt
));
946 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
948 rv
= stmt
->BindStringByName(NS_LITERAL_CSTRING("version"), mVersion
);
949 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
951 if (NS_FAILED(stmt
->Execute())) {
952 return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR
;
959 SetVersionHelper::GetSuccessResult(JSContext
* aCx
,
963 if (!DatabaseInfo::Get(mDatabase
->Id(), &info
)) {
964 NS_ERROR("This should never fail!");
965 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
967 info
->version
= mVersion
;
969 nsresult rv
= WrapNative(aCx
, NS_ISUPPORTS_CAST(nsPIDOMEventTarget
*,
972 NS_ENSURE_SUCCESS(rv
, rv
);
978 CreateObjectStoreHelper::DoDatabaseWork(mozIStorageConnection
* aConnection
)
980 nsCOMPtr
<mozIStorageStatement
> stmt
=
981 mTransaction
->GetCachedStatement(NS_LITERAL_CSTRING(
982 "INSERT INTO object_store (id, name, key_path, auto_increment) "
983 "VALUES (:id, :name, :key_path, :auto_increment)"
985 NS_ENSURE_TRUE(stmt
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
987 mozStorageStatementScoper
scoper(stmt
);
989 nsresult rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("id"),
991 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
993 rv
= stmt
->BindStringByName(NS_LITERAL_CSTRING("name"), mObjectStore
->Name());
994 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
996 rv
= stmt
->BindStringByName(NS_LITERAL_CSTRING("key_path"),
997 mObjectStore
->KeyPath());
998 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
1000 rv
= stmt
->BindInt32ByName(NS_LITERAL_CSTRING("auto_increment"),
1001 mObjectStore
->IsAutoIncrement() ? 1 : 0);
1002 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
1004 rv
= stmt
->Execute();
1005 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
1011 DeleteObjectStoreHelper::DoDatabaseWork(mozIStorageConnection
* aConnection
)
1013 nsCOMPtr
<mozIStorageStatement
> stmt
=
1014 mTransaction
->GetCachedStatement(NS_LITERAL_CSTRING(
1015 "DELETE FROM object_store "
1018 NS_ENSURE_TRUE(stmt
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
1020 mozStorageStatementScoper
scoper(stmt
);
1022 nsresult rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("id"), mObjectStoreId
);
1023 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
1025 rv
= stmt
->Execute();
1026 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);