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(nsIWritableVariant
* aResult
);
92 class CreateObjectStoreHelper
: public AsyncConnectionHelper
95 CreateObjectStoreHelper(IDBTransaction
* aTransaction
,
96 IDBObjectStore
* aObjectStore
)
97 : AsyncConnectionHelper(aTransaction
, nsnull
), mObjectStore(aObjectStore
)
100 nsresult
DoDatabaseWork(mozIStorageConnection
* aConnection
);
101 nsresult
OnSuccess(nsIDOMEventTarget
* aTarget
);
102 void OnError(nsIDOMEventTarget
* aTarget
, nsresult aErrorCode
);
104 void ReleaseMainThreadObjects()
106 mObjectStore
= nsnull
;
107 AsyncConnectionHelper::ReleaseMainThreadObjects();
111 nsRefPtr
<IDBObjectStore
> mObjectStore
;
114 class DeleteObjectStoreHelper
: public AsyncConnectionHelper
117 DeleteObjectStoreHelper(IDBTransaction
* aTransaction
,
118 PRInt64 aObjectStoreId
)
119 : AsyncConnectionHelper(aTransaction
, nsnull
), mObjectStoreId(aObjectStoreId
)
122 nsresult
DoDatabaseWork(mozIStorageConnection
* aConnection
);
123 nsresult
OnSuccess(nsIDOMEventTarget
* aTarget
);
124 void OnError(nsIDOMEventTarget
* aTarget
, nsresult aErrorCode
);
128 PRInt64 mObjectStoreId
;
135 AutoFree(void* aPtr
) : mPtr(aPtr
) { }
136 ~AutoFree() { NS_Free(mPtr
); }
142 class AutoRemoveObjectStore
145 AutoRemoveObjectStore(PRUint32 aId
, const nsAString
& aName
)
146 : mId(aId
), mName(aName
)
149 ~AutoRemoveObjectStore()
152 ObjectStoreInfo::Remove(mId
, mName
);
168 ConvertVariantToStringArray(nsIVariant
* aVariant
,
169 nsTArray
<nsString
>& aStringArray
)
173 NS_ASSERTION(NS_SUCCEEDED(aVariant
->GetDataType(&type
)) &&
174 type
== nsIDataType::VTYPE_ARRAY
, "Bad arg!");
182 nsresult rv
= aVariant
->GetAsArray(&valueType
, &iid
, &valueCount
, &rawArray
);
183 NS_ENSURE_SUCCESS(rv
, rv
);
185 AutoFree
af(rawArray
);
187 // Just delete anything that we don't expect and return.
188 if (valueType
!= nsIDataType::VTYPE_WCHAR_STR
) {
190 case nsIDataType::VTYPE_ID
:
191 case nsIDataType::VTYPE_CHAR_STR
: {
192 char** charArray
= reinterpret_cast<char**>(rawArray
);
193 for (PRUint32 index
= 0; index
< valueCount
; index
++) {
194 if (charArray
[index
]) {
195 NS_Free(charArray
[index
]);
200 case nsIDataType::VTYPE_INTERFACE
:
201 case nsIDataType::VTYPE_INTERFACE_IS
: {
202 nsISupports
** supportsArray
= reinterpret_cast<nsISupports
**>(rawArray
);
203 for (PRUint32 index
= 0; index
< valueCount
; index
++) {
204 NS_IF_RELEASE(supportsArray
[index
]);
209 // The other types are primitives that do not need to be freed.
213 return NS_ERROR_ILLEGAL_VALUE
;
216 PRUnichar
** strings
= reinterpret_cast<PRUnichar
**>(rawArray
);
218 for (PRUint32 index
= 0; index
< valueCount
; index
++) {
219 nsString
* newString
= aStringArray
.AppendElement();
222 NS_ERROR("Out of memory?");
223 for (; index
< valueCount
; index
++) {
224 NS_Free(strings
[index
]);
226 return NS_ERROR_OUT_OF_MEMORY
;
229 newString
->Adopt(strings
[index
], -1);
235 } // anonymous namespace
238 already_AddRefed
<IDBDatabase
>
239 IDBDatabase::Create(nsIScriptContext
* aScriptContext
,
240 nsPIDOMWindow
* aOwner
,
241 DatabaseInfo
* aDatabaseInfo
,
242 const nsACString
& aASCIIOrigin
)
244 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
245 NS_ASSERTION(aDatabaseInfo
, "Null pointer!");
246 NS_ASSERTION(!aASCIIOrigin
.IsEmpty(), "Empty origin!");
248 nsRefPtr
<IDBDatabase
> db(new IDBDatabase());
250 db
->mScriptContext
= aScriptContext
;
253 db
->mDatabaseId
= aDatabaseInfo
->id
;
254 db
->mName
= aDatabaseInfo
->name
;
255 db
->mFilePath
= aDatabaseInfo
->filePath
;
256 db
->mASCIIOrigin
= aASCIIOrigin
;
258 IndexedDatabaseManager
* mgr
= IndexedDatabaseManager::Get();
259 NS_ASSERTION(mgr
, "This should never be null!");
261 if (!mgr
->RegisterDatabase(db
)) {
262 // Either out of memory or shutting down.
269 IDBDatabase::IDBDatabase()
275 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
277 if (!gDatabaseInstanceCount
++) {
278 NS_ASSERTION(!gPromptHelpersMutex
, "Should be null!");
279 gPromptHelpersMutex
= new mozilla::Mutex("IDBDatabase gPromptHelpersMutex");
283 IDBDatabase::~IDBDatabase()
285 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
290 IndexedDatabaseManager
* mgr
= IndexedDatabaseManager::Get();
292 mgr
->UnregisterDatabase(this);
296 if (mDatabaseId
&& !mInvalidated
) {
298 if (!DatabaseInfo::Get(mDatabaseId
, &info
)) {
299 NS_ERROR("This should never fail!");
302 NS_ASSERTION(info
->referenceCount
, "Bad reference count!");
303 if (--info
->referenceCount
== 0) {
304 DatabaseInfo::Remove(mDatabaseId
);
308 if (mListenerManager
) {
309 mListenerManager
->Disconnect();
312 if (!--gDatabaseInstanceCount
) {
313 NS_ASSERTION(gPromptHelpersMutex
, "Should not be null!");
315 delete gPromptHelpers
;
316 gPromptHelpers
= nsnull
;
318 delete gPromptHelpersMutex
;
319 gPromptHelpersMutex
= nsnull
;
324 IDBDatabase::IsQuotaDisabled()
326 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
327 NS_ASSERTION(gPromptHelpersMutex
, "This should never be null!");
329 MutexAutoLock
lock(*gPromptHelpersMutex
);
331 if (!gPromptHelpers
) {
332 gPromptHelpers
= new nsAutoTArray
<nsRefPtr
<CheckQuotaHelper
>, 10>();
335 CheckQuotaHelper
* foundHelper
= nsnull
;
337 PRUint32 count
= gPromptHelpers
->Length();
338 for (PRUint32 index
= 0; index
< count
; index
++) {
339 nsRefPtr
<CheckQuotaHelper
>& helper
= gPromptHelpers
->ElementAt(index
);
340 if (helper
->WindowSerial() == Owner()->GetSerial()) {
341 foundHelper
= helper
;
347 nsRefPtr
<CheckQuotaHelper
>* newHelper
= gPromptHelpers
->AppendElement();
349 NS_WARNING("Out of memory!");
352 *newHelper
= new CheckQuotaHelper(this, *gPromptHelpersMutex
);
353 foundHelper
= *newHelper
;
356 // Unlock before calling out to XPCOM.
357 MutexAutoUnlock
unlock(*gPromptHelpersMutex
);
359 nsresult rv
= NS_DispatchToMainThread(foundHelper
, NS_DISPATCH_NORMAL
);
360 NS_ENSURE_SUCCESS(rv
, false);
364 return foundHelper
->PromptAndReturnQuotaIsDisabled();
368 IDBDatabase::Invalidate()
370 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
371 NS_ASSERTION(gPromptHelpersMutex
, "This should never be null!");
373 // Make sure we're closed too.
376 // Cancel any quota prompts that are currently being displayed.
378 MutexAutoLock
lock(*gPromptHelpersMutex
);
380 if (gPromptHelpers
) {
381 PRUint32 count
= gPromptHelpers
->Length();
382 for (PRUint32 index
= 0; index
< count
; index
++) {
383 nsRefPtr
<CheckQuotaHelper
>& helper
= gPromptHelpers
->ElementAt(index
);
384 if (helper
->WindowSerial() == Owner()->GetSerial()) {
392 if (!PR_AtomicSet(&mInvalidated
, 1)) {
394 if (!DatabaseInfo::Get(mDatabaseId
, &info
)) {
395 NS_ERROR("This should never fail!");
398 NS_ASSERTION(info
->referenceCount
, "Bad reference count!");
399 if (--info
->referenceCount
== 0) {
400 DatabaseInfo::Remove(mDatabaseId
);
406 IDBDatabase::IsInvalidated()
408 return !!mInvalidated
;
412 IDBDatabase::CloseInternal()
414 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
417 IndexedDatabaseManager
* mgr
= IndexedDatabaseManager::Get();
419 mgr
->OnDatabaseClosed(this);
426 IDBDatabase::IsClosed()
428 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
433 IDBDatabase::OnUnlink()
435 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
436 NS_ASSERTION(!mOwner
, "Should have been cleared already!");
438 // We've been unlinked, at the very least we should be able to prevent further
439 // transactions from starting and unblock any other SetVersion callers.
442 // No reason for the IndexedDatabaseManager to track us any longer.
443 IndexedDatabaseManager
* mgr
= IndexedDatabaseManager::Get();
445 mgr
->UnregisterDatabase(this);
447 // Don't try to unregister again in the destructor.
452 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBDatabase
)
454 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBDatabase
,
455 nsDOMEventTargetHelper
)
456 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnErrorListener
)
457 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnVersionChangeListener
)
458 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
460 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBDatabase
,
461 nsDOMEventTargetHelper
)
462 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnErrorListener
)
463 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnVersionChangeListener
)
467 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
469 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBDatabase
)
470 NS_INTERFACE_MAP_ENTRY(nsIIDBDatabase
)
471 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBDatabase
)
472 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper
)
474 NS_IMPL_ADDREF_INHERITED(IDBDatabase
, nsDOMEventTargetHelper
)
475 NS_IMPL_RELEASE_INHERITED(IDBDatabase
, nsDOMEventTargetHelper
)
477 DOMCI_DATA(IDBDatabase
, IDBDatabase
)
480 IDBDatabase::GetName(nsAString
& aName
)
482 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
488 IDBDatabase::GetVersion(nsAString
& aVersion
)
490 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
492 if (!DatabaseInfo::Get(mDatabaseId
, &info
)) {
493 NS_ERROR("This should never fail!");
495 aVersion
.Assign(info
->version
);
500 IDBDatabase::GetObjectStoreNames(nsIDOMDOMStringList
** aObjectStores
)
502 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
505 if (!DatabaseInfo::Get(mDatabaseId
, &info
)) {
506 NS_ERROR("This should never fail!");
509 nsAutoTArray
<nsString
, 10> objectStoreNames
;
510 if (!info
->GetObjectStoreNames(objectStoreNames
)) {
511 NS_WARNING("Couldn't get names!");
512 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
515 nsRefPtr
<nsDOMStringList
> list(new nsDOMStringList());
516 PRUint32 count
= objectStoreNames
.Length();
517 for (PRUint32 index
= 0; index
< count
; index
++) {
518 NS_ENSURE_TRUE(list
->Add(objectStoreNames
[index
]),
519 NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
522 list
.forget(aObjectStores
);
527 IDBDatabase::CreateObjectStore(const nsAString
& aName
,
528 const jsval
& aOptions
,
530 nsIIDBObjectStore
** _retval
)
532 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
534 if (aName
.IsEmpty()) {
535 // XXX Update spec for a real error code here.
536 return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR
;
539 IDBTransaction
* transaction
= AsyncConnectionHelper::GetCurrentTransaction();
542 transaction
->Mode() != nsIIDBTransaction::VERSION_CHANGE
) {
543 return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR
;
546 DatabaseInfo
* databaseInfo
;
547 if (!DatabaseInfo::Get(mDatabaseId
, &databaseInfo
)) {
548 NS_ERROR("This should never fail!");
551 if (databaseInfo
->ContainsStoreName(aName
)) {
552 return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR
;
556 bool autoIncrement
= false;
558 if (!JSVAL_IS_VOID(aOptions
) && !JSVAL_IS_NULL(aOptions
)) {
559 if (JSVAL_IS_PRIMITIVE(aOptions
)) {
560 // XXX Update spec for a real code here
561 return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR
;
564 NS_ASSERTION(JSVAL_IS_OBJECT(aOptions
), "Huh?!");
565 JSObject
* options
= JSVAL_TO_OBJECT(aOptions
);
567 js::AutoIdArray
ids(aCx
, JS_Enumerate(aCx
, options
));
569 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
572 for (size_t index
= 0; index
< ids
.length(); index
++) {
573 jsid id
= ids
[index
];
575 if (id
!= nsDOMClassInfo::sKeyPath_id
&&
576 id
!= nsDOMClassInfo::sAutoIncrement_id
) {
577 // XXX Update spec for a real code here
578 return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR
;
582 if (!JS_GetPropertyById(aCx
, options
, id
, &val
)) {
583 NS_WARNING("JS_GetPropertyById failed!");
584 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
587 if (id
== nsDOMClassInfo::sKeyPath_id
) {
588 JSString
* str
= JS_ValueToString(aCx
, val
);
590 NS_WARNING("JS_ValueToString failed!");
591 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
593 nsDependentJSString dependentKeyPath
;
594 if (!dependentKeyPath
.init(aCx
, str
)) {
595 NS_WARNING("Initializing keyPath failed!");
596 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
598 keyPath
= dependentKeyPath
;
600 else if (id
== nsDOMClassInfo::sAutoIncrement_id
) {
602 if (!JS_ValueToBoolean(aCx
, val
, &boolVal
)) {
603 NS_WARNING("JS_ValueToBoolean failed!");
604 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
606 autoIncrement
= !!boolVal
;
609 NS_NOTREACHED("Shouldn't be able to get here!");
614 nsAutoPtr
<ObjectStoreInfo
> newInfo(new ObjectStoreInfo());
616 newInfo
->name
= aName
;
617 newInfo
->id
= databaseInfo
->nextObjectStoreId
++;
618 newInfo
->keyPath
= keyPath
;
619 newInfo
->autoIncrement
= autoIncrement
;
620 newInfo
->databaseId
= mDatabaseId
;
622 if (!ObjectStoreInfo::Put(newInfo
)) {
623 NS_WARNING("Put failed!");
624 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
626 ObjectStoreInfo
* objectStoreInfo
= newInfo
.forget();
628 // Don't leave this in the hash if we fail below!
629 AutoRemoveObjectStore
autoRemove(mDatabaseId
, aName
);
631 nsRefPtr
<IDBObjectStore
> objectStore
=
632 transaction
->GetOrCreateObjectStore(aName
, objectStoreInfo
);
633 NS_ENSURE_TRUE(objectStore
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
635 nsRefPtr
<CreateObjectStoreHelper
> helper
=
636 new CreateObjectStoreHelper(transaction
, objectStore
);
638 nsresult rv
= helper
->DispatchToTransactionPool();
639 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
643 objectStore
.forget(_retval
);
648 IDBDatabase::DeleteObjectStore(const nsAString
& aName
)
650 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
652 IDBTransaction
* transaction
= AsyncConnectionHelper::GetCurrentTransaction();
655 transaction
->Mode() != nsIIDBTransaction::VERSION_CHANGE
) {
656 return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR
;
659 ObjectStoreInfo
* objectStoreInfo
;
660 if (!ObjectStoreInfo::Get(mDatabaseId
, aName
, &objectStoreInfo
)) {
661 return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR
;
664 nsRefPtr
<DeleteObjectStoreHelper
> helper
=
665 new DeleteObjectStoreHelper(transaction
, objectStoreInfo
->id
);
666 nsresult rv
= helper
->DispatchToTransactionPool();
667 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
669 ObjectStoreInfo::Remove(mDatabaseId
, aName
);
674 IDBDatabase::SetVersion(const nsAString
& aVersion
,
676 nsIIDBRequest
** _retval
)
678 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
681 // XXX Update spec for a real error code here.
682 return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR
;
686 if (!DatabaseInfo::Get(mDatabaseId
, &info
)) {
687 NS_ERROR("This should never fail!");
690 // Lock the whole database.
691 nsTArray
<nsString
> storesToOpen
;
692 nsRefPtr
<IDBTransaction
> transaction
=
693 IDBTransaction::Create(this, storesToOpen
, IDBTransaction::VERSION_CHANGE
,
694 kDefaultDatabaseTimeoutSeconds
, true);
695 NS_ENSURE_TRUE(transaction
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
697 nsRefPtr
<IDBVersionChangeRequest
> request
=
698 IDBVersionChangeRequest::Create(static_cast<nsPIDOMEventTarget
*>(this),
699 ScriptContext(), Owner(), transaction
);
700 NS_ENSURE_TRUE(request
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
702 nsRefPtr
<SetVersionHelper
> helper
=
703 new SetVersionHelper(transaction
, request
, aVersion
);
705 IndexedDatabaseManager
* mgr
= IndexedDatabaseManager::Get();
706 NS_ASSERTION(mgr
, "This should never be null!");
708 nsresult rv
= mgr
->SetDatabaseVersion(this, request
, aVersion
, helper
);
709 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
711 request
.forget(_retval
);
716 IDBDatabase::Transaction(nsIVariant
* aStoreNames
,
720 PRUint8 aOptionalArgCount
,
721 nsIIDBTransaction
** _retval
)
723 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
725 if (IndexedDatabaseManager::IsShuttingDown()) {
726 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
730 return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR
;
733 if (aOptionalArgCount
) {
734 if (aMode
!= nsIIDBTransaction::READ_WRITE
&&
735 aMode
!= nsIIDBTransaction::READ_ONLY
&&
736 aMode
!= nsIIDBTransaction::SNAPSHOT_READ
) {
737 return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR
;
739 if (aMode
== nsIIDBTransaction::SNAPSHOT_READ
) {
740 NS_NOTYETIMPLEMENTED("Implement me!");
741 return NS_ERROR_NOT_IMPLEMENTED
;
745 aMode
= nsIIDBTransaction::READ_ONLY
;
748 if (aOptionalArgCount
<= 1) {
749 aTimeout
= kDefaultDatabaseTimeoutSeconds
;
753 nsresult rv
= aStoreNames
->GetDataType(&type
);
754 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
757 if (!DatabaseInfo::Get(mDatabaseId
, &info
)) {
758 NS_ERROR("This should never fail!");
761 nsTArray
<nsString
> storesToOpen
;
764 case nsIDataType::VTYPE_VOID
:
765 case nsIDataType::VTYPE_EMPTY
:
766 case nsIDataType::VTYPE_EMPTY_ARRAY
: {
767 // Empty, request all object stores
768 if (!info
->GetObjectStoreNames(storesToOpen
)) {
769 NS_WARNING("Out of memory?");
770 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
774 case nsIDataType::VTYPE_WSTRING_SIZE_IS
: {
777 rv
= aStoreNames
->GetAsAString(name
);
778 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
780 if (!info
->ContainsStoreName(name
)) {
781 return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR
;
784 if (!storesToOpen
.AppendElement(name
)) {
785 NS_WARNING("Out of memory?");
786 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
790 case nsIDataType::VTYPE_ARRAY
: {
791 nsTArray
<nsString
> names
;
792 rv
= ConvertVariantToStringArray(aStoreNames
, names
);
793 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
795 PRUint32 nameCount
= names
.Length();
796 for (PRUint32 nameIndex
= 0; nameIndex
< nameCount
; nameIndex
++) {
797 nsString
& name
= names
[nameIndex
];
799 if (!info
->ContainsStoreName(name
)) {
800 return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR
;
803 if (!storesToOpen
.AppendElement(name
)) {
804 NS_WARNING("Out of memory?");
805 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
808 NS_ASSERTION(nameCount
== storesToOpen
.Length(), "Should have bailed!");
811 case nsIDataType::VTYPE_INTERFACE
:
812 case nsIDataType::VTYPE_INTERFACE_IS
: {
813 nsCOMPtr
<nsISupports
> supports
;
815 rv
= aStoreNames
->GetAsInterface(&iid
, getter_AddRefs(supports
));
816 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
820 nsCOMPtr
<nsIDOMDOMStringList
> stringList(do_QueryInterface(supports
));
822 // We don't support anything other than nsIDOMDOMStringList.
823 return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR
;
826 PRUint32 stringCount
;
827 rv
= stringList
->GetLength(&stringCount
);
828 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
830 for (PRUint32 stringIndex
= 0; stringIndex
< stringCount
; stringIndex
++) {
832 rv
= stringList
->Item(stringIndex
, name
);
833 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
835 if (!info
->ContainsStoreName(name
)) {
836 return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR
;
839 if (!storesToOpen
.AppendElement(name
)) {
840 NS_WARNING("Out of memory?");
841 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
847 return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR
;
850 nsRefPtr
<IDBTransaction
> transaction
=
851 IDBTransaction::Create(this, storesToOpen
, aMode
,
852 kDefaultDatabaseTimeoutSeconds
);
853 NS_ENSURE_TRUE(transaction
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
855 transaction
.forget(_retval
);
862 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
866 NS_ASSERTION(mClosed
, "Should have set the closed flag!");
871 IDBDatabase::SetOnerror(nsIDOMEventListener
* aErrorListener
)
873 return RemoveAddEventListener(NS_LITERAL_STRING(ERROR_EVT_STR
),
874 mOnErrorListener
, aErrorListener
);
878 IDBDatabase::GetOnerror(nsIDOMEventListener
** aErrorListener
)
880 return GetInnerEventListener(mOnErrorListener
, aErrorListener
);
884 IDBDatabase::SetOnversionchange(nsIDOMEventListener
* aVersionChangeListener
)
886 return RemoveAddEventListener(NS_LITERAL_STRING(VERSIONCHANGE_EVT_STR
),
887 mOnVersionChangeListener
,
888 aVersionChangeListener
);
892 IDBDatabase::GetOnversionchange(nsIDOMEventListener
** aVersionChangeListener
)
894 return GetInnerEventListener(mOnVersionChangeListener
,
895 aVersionChangeListener
);
899 IDBDatabase::PostHandleEvent(nsEventChainPostVisitor
& aVisitor
)
901 NS_ENSURE_TRUE(aVisitor
.mDOMEvent
, NS_ERROR_UNEXPECTED
);
903 if (aVisitor
.mEventStatus
!= nsEventStatus_eConsumeNoDefault
) {
904 nsCOMPtr
<nsIDOMEvent
> duplicateEvent
=
905 IDBErrorEvent::MaybeDuplicate(aVisitor
.mDOMEvent
);
907 if (duplicateEvent
) {
908 nsCOMPtr
<nsIDOMEventTarget
> target(do_QueryInterface(mOwner
));
909 NS_ASSERTION(target
, "How can this happen?!");
912 target
->DispatchEvent(duplicateEvent
, &dummy
);
920 SetVersionHelper::DoDatabaseWork(mozIStorageConnection
* aConnection
)
922 NS_PRECONDITION(aConnection
, "Passing a null connection!");
924 nsCOMPtr
<mozIStorageStatement
> stmt
;
925 nsresult rv
= aConnection
->CreateStatement(NS_LITERAL_CSTRING(
927 "SET version = :version"
928 ), getter_AddRefs(stmt
));
929 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
931 rv
= stmt
->BindStringByName(NS_LITERAL_CSTRING("version"), mVersion
);
932 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
934 if (NS_FAILED(stmt
->Execute())) {
935 return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR
;
942 SetVersionHelper::GetSuccessResult(nsIWritableVariant
* aResult
)
945 if (!DatabaseInfo::Get(mDatabase
->Id(), &info
)) {
946 NS_ERROR("This should never fail!");
947 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
;
949 info
->version
= mVersion
;
951 aResult
->SetAsISupports(static_cast<nsPIDOMEventTarget
*>(mTransaction
));
956 CreateObjectStoreHelper::DoDatabaseWork(mozIStorageConnection
* aConnection
)
958 nsCOMPtr
<mozIStorageStatement
> stmt
=
959 mTransaction
->GetCachedStatement(NS_LITERAL_CSTRING(
960 "INSERT INTO object_store (id, name, key_path, auto_increment) "
961 "VALUES (:id, :name, :key_path, :auto_increment)"
963 NS_ENSURE_TRUE(stmt
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
965 mozStorageStatementScoper
scoper(stmt
);
967 nsresult rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("id"),
969 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
971 rv
= stmt
->BindStringByName(NS_LITERAL_CSTRING("name"), mObjectStore
->Name());
972 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
974 rv
= stmt
->BindStringByName(NS_LITERAL_CSTRING("key_path"),
975 mObjectStore
->KeyPath());
976 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
978 rv
= stmt
->BindInt32ByName(NS_LITERAL_CSTRING("auto_increment"),
979 mObjectStore
->IsAutoIncrement() ? 1 : 0);
980 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
982 rv
= stmt
->Execute();
983 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
989 CreateObjectStoreHelper::OnSuccess(nsIDOMEventTarget
* aTarget
)
991 NS_ASSERTION(!aTarget
, "Huh?!");
996 CreateObjectStoreHelper::OnError(nsIDOMEventTarget
* aTarget
,
999 NS_ASSERTION(!aTarget
, "Huh?!");
1003 DeleteObjectStoreHelper::DoDatabaseWork(mozIStorageConnection
* aConnection
)
1005 nsCOMPtr
<mozIStorageStatement
> stmt
=
1006 mTransaction
->GetCachedStatement(NS_LITERAL_CSTRING(
1007 "DELETE FROM object_store "
1010 NS_ENSURE_TRUE(stmt
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
1012 mozStorageStatementScoper
scoper(stmt
);
1014 nsresult rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("id"), mObjectStoreId
);
1015 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
1017 rv
= stmt
->Execute();
1018 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
1024 DeleteObjectStoreHelper::OnSuccess(nsIDOMEventTarget
* aTarget
)
1026 NS_ASSERTION(!aTarget
, "Huh?!");
1032 DeleteObjectStoreHelper::OnError(nsIDOMEventTarget
* aTarget
,
1033 nsresult aErrorCode
)
1035 NS_NOTREACHED("Removing an object store should never fail here!");