Merge mozilla-central to tracemonkey.
[mozilla-central.git] / dom / indexedDB / IDBDatabase.cpp
blobddf3223436a829601c08110bd67118881e57c78e
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
14 * License.
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.
23 * Contributor(s):
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"
42 #include "jscntxt.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"
55 #include "IDBIndex.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
65 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
77 public:
78 SetVersionHelper(IDBTransaction* aTransaction,
79 IDBRequest* aRequest,
80 const nsAString& aVersion)
81 : AsyncConnectionHelper(aTransaction, aRequest), mVersion(aVersion)
82 { }
84 nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
85 nsresult GetSuccessResult(nsIWritableVariant* aResult);
87 private:
88 // In-params
89 nsString mVersion;
92 class CreateObjectStoreHelper : public AsyncConnectionHelper
94 public:
95 CreateObjectStoreHelper(IDBTransaction* aTransaction,
96 IDBObjectStore* aObjectStore)
97 : AsyncConnectionHelper(aTransaction, nsnull), mObjectStore(aObjectStore)
98 { }
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();
110 private:
111 nsRefPtr<IDBObjectStore> mObjectStore;
114 class DeleteObjectStoreHelper : public AsyncConnectionHelper
116 public:
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);
126 private:
127 // In-params.
128 PRInt64 mObjectStoreId;
131 NS_STACK_CLASS
132 class AutoFree
134 public:
135 AutoFree(void* aPtr) : mPtr(aPtr) { }
136 ~AutoFree() { NS_Free(mPtr); }
137 private:
138 void* mPtr;
141 NS_STACK_CLASS
142 class AutoRemoveObjectStore
144 public:
145 AutoRemoveObjectStore(PRUint32 aId, const nsAString& aName)
146 : mId(aId), mName(aName)
149 ~AutoRemoveObjectStore()
151 if (mId) {
152 ObjectStoreInfo::Remove(mId, mName);
156 void forget()
158 mId = 0;
161 private:
162 PRUint32 mId;
163 nsString mName;
166 inline
167 nsresult
168 ConvertVariantToStringArray(nsIVariant* aVariant,
169 nsTArray<nsString>& aStringArray)
171 #ifdef DEBUG
172 PRUint16 type;
173 NS_ASSERTION(NS_SUCCEEDED(aVariant->GetDataType(&type)) &&
174 type == nsIDataType::VTYPE_ARRAY, "Bad arg!");
175 #endif
177 PRUint16 valueType;
178 nsIID iid;
179 PRUint32 valueCount;
180 void* rawArray;
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) {
189 switch (valueType) {
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]);
198 } break;
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]);
206 } break;
208 default: {
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();
221 if (!newString) {
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);
232 return NS_OK;
235 } // anonymous namespace
237 // static
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;
251 db->mOwner = aOwner;
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.
263 return nsnull;
266 return db.forget();
269 IDBDatabase::IDBDatabase()
270 : mDatabaseId(0),
271 mInvalidated(0),
272 mRegistered(false),
273 mClosed(false)
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!");
287 if (mRegistered) {
288 CloseInternal();
290 IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
291 if (mgr) {
292 mgr->UnregisterDatabase(this);
296 if (mDatabaseId && !mInvalidated) {
297 DatabaseInfo* info;
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;
323 bool
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;
342 break;
346 if (!foundHelper) {
347 nsRefPtr<CheckQuotaHelper>* newHelper = gPromptHelpers->AppendElement();
348 if (!newHelper) {
349 NS_WARNING("Out of memory!");
350 return false;
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();
367 void
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.
374 Close();
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()) {
385 helper->Cancel();
386 break;
392 if (!PR_AtomicSet(&mInvalidated, 1)) {
393 DatabaseInfo* info;
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);
405 bool
406 IDBDatabase::IsInvalidated()
408 return !!mInvalidated;
411 void
412 IDBDatabase::CloseInternal()
414 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
416 if (!mClosed) {
417 IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
418 if (mgr) {
419 mgr->OnDatabaseClosed(this);
421 mClosed = true;
425 bool
426 IDBDatabase::IsClosed()
428 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
429 return mClosed;
432 void
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.
440 Close();
442 // No reason for the IndexedDatabaseManager to track us any longer.
443 IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
444 if (mgr) {
445 mgr->UnregisterDatabase(this);
447 // Don't try to unregister again in the destructor.
448 mRegistered = false;
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)
465 // Do some cleanup.
466 tmp->OnUnlink();
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)
479 NS_IMETHODIMP
480 IDBDatabase::GetName(nsAString& aName)
482 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
483 aName.Assign(mName);
484 return NS_OK;
487 NS_IMETHODIMP
488 IDBDatabase::GetVersion(nsAString& aVersion)
490 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
491 DatabaseInfo* info;
492 if (!DatabaseInfo::Get(mDatabaseId, &info)) {
493 NS_ERROR("This should never fail!");
495 aVersion.Assign(info->version);
496 return NS_OK;
499 NS_IMETHODIMP
500 IDBDatabase::GetObjectStoreNames(nsIDOMDOMStringList** aObjectStores)
502 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
504 DatabaseInfo* info;
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);
523 return NS_OK;
526 NS_IMETHODIMP
527 IDBDatabase::CreateObjectStore(const nsAString& aName,
528 const jsval& aOptions,
529 JSContext* aCx,
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();
541 if (!transaction ||
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;
555 nsString keyPath;
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));
568 if (!ids) {
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;
581 jsval val;
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);
589 if (!str) {
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) {
601 JSBool boolVal;
602 if (!JS_ValueToBoolean(aCx, val, &boolVal)) {
603 NS_WARNING("JS_ValueToBoolean failed!");
604 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
606 autoIncrement = !!boolVal;
608 else {
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);
641 autoRemove.forget();
643 objectStore.forget(_retval);
644 return NS_OK;
647 NS_IMETHODIMP
648 IDBDatabase::DeleteObjectStore(const nsAString& aName)
650 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
652 IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction();
654 if (!transaction ||
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);
670 return NS_OK;
673 NS_IMETHODIMP
674 IDBDatabase::SetVersion(const nsAString& aVersion,
675 JSContext* aCx,
676 nsIIDBRequest** _retval)
678 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
680 if (mClosed) {
681 // XXX Update spec for a real error code here.
682 return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
685 DatabaseInfo* info;
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);
712 return NS_OK;
715 NS_IMETHODIMP
716 IDBDatabase::Transaction(nsIVariant* aStoreNames,
717 PRUint16 aMode,
718 PRUint32 aTimeout,
719 JSContext* aCx,
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;
729 if (mClosed) {
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;
744 else {
745 aMode = nsIIDBTransaction::READ_ONLY;
748 if (aOptionalArgCount <= 1) {
749 aTimeout = kDefaultDatabaseTimeoutSeconds;
752 PRUint16 type;
753 nsresult rv = aStoreNames->GetDataType(&type);
754 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
756 DatabaseInfo* info;
757 if (!DatabaseInfo::Get(mDatabaseId, &info)) {
758 NS_ERROR("This should never fail!");
761 nsTArray<nsString> storesToOpen;
763 switch (type) {
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;
772 } break;
774 case nsIDataType::VTYPE_WSTRING_SIZE_IS: {
775 // Single name
776 nsString name;
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;
788 } break;
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!");
809 } break;
811 case nsIDataType::VTYPE_INTERFACE:
812 case nsIDataType::VTYPE_INTERFACE_IS: {
813 nsCOMPtr<nsISupports> supports;
814 nsID *iid;
815 rv = aStoreNames->GetAsInterface(&iid, getter_AddRefs(supports));
816 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
818 NS_Free(iid);
820 nsCOMPtr<nsIDOMDOMStringList> stringList(do_QueryInterface(supports));
821 if (!stringList) {
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++) {
831 nsString name;
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;
844 } break;
846 default:
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);
856 return NS_OK;
859 NS_IMETHODIMP
860 IDBDatabase::Close()
862 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
864 CloseInternal();
866 NS_ASSERTION(mClosed, "Should have set the closed flag!");
867 return NS_OK;
870 NS_IMETHODIMP
871 IDBDatabase::SetOnerror(nsIDOMEventListener* aErrorListener)
873 return RemoveAddEventListener(NS_LITERAL_STRING(ERROR_EVT_STR),
874 mOnErrorListener, aErrorListener);
877 NS_IMETHODIMP
878 IDBDatabase::GetOnerror(nsIDOMEventListener** aErrorListener)
880 return GetInnerEventListener(mOnErrorListener, aErrorListener);
883 NS_IMETHODIMP
884 IDBDatabase::SetOnversionchange(nsIDOMEventListener* aVersionChangeListener)
886 return RemoveAddEventListener(NS_LITERAL_STRING(VERSIONCHANGE_EVT_STR),
887 mOnVersionChangeListener,
888 aVersionChangeListener);
891 NS_IMETHODIMP
892 IDBDatabase::GetOnversionchange(nsIDOMEventListener** aVersionChangeListener)
894 return GetInnerEventListener(mOnVersionChangeListener,
895 aVersionChangeListener);
898 nsresult
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?!");
911 PRBool dummy;
912 target->DispatchEvent(duplicateEvent, &dummy);
916 return NS_OK;
919 nsresult
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(
926 "UPDATE database "
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;
938 return NS_OK;
941 nsresult
942 SetVersionHelper::GetSuccessResult(nsIWritableVariant* aResult)
944 DatabaseInfo* info;
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));
952 return NS_OK;
955 nsresult
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"),
968 mObjectStore->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);
985 return NS_OK;
988 nsresult
989 CreateObjectStoreHelper::OnSuccess(nsIDOMEventTarget* aTarget)
991 NS_ASSERTION(!aTarget, "Huh?!");
992 return NS_OK;
995 void
996 CreateObjectStoreHelper::OnError(nsIDOMEventTarget* aTarget,
997 nsresult aErrorCode)
999 NS_ASSERTION(!aTarget, "Huh?!");
1002 nsresult
1003 DeleteObjectStoreHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
1005 nsCOMPtr<mozIStorageStatement> stmt =
1006 mTransaction->GetCachedStatement(NS_LITERAL_CSTRING(
1007 "DELETE FROM object_store "
1008 "WHERE id = :id "
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);
1020 return NS_OK;
1023 nsresult
1024 DeleteObjectStoreHelper::OnSuccess(nsIDOMEventTarget* aTarget)
1026 NS_ASSERTION(!aTarget, "Huh?!");
1028 return NS_OK;
1031 void
1032 DeleteObjectStoreHelper::OnError(nsIDOMEventTarget* aTarget,
1033 nsresult aErrorCode)
1035 NS_NOTREACHED("Removing an object store should never fail here!");