From bee1f84b2f5df59a85a186040f3a808bee1dc77c Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Thu, 20 Oct 2011 12:10:56 -0400 Subject: [PATCH] Bug 687361: Implement the new IndexedDB setVersion API. r=bent --HG-- rename : dom/indexedDB/nsIIDBVersionChangeRequest.idl => dom/indexedDB/nsIIDBOpenDBRequest.idl --- content/base/src/nsGkAtomList.h | 2 + dom/base/domerr.msg | 6 +- dom/base/nsDOMClassInfo.cpp | 6 +- dom/base/nsDOMClassInfoClasses.h | 2 +- dom/base/nsDOMError.h | 10 +- dom/indexedDB/AsyncConnectionHelper.cpp | 61 +- dom/indexedDB/AsyncConnectionHelper.h | 49 +- dom/indexedDB/CheckPermissionsHelper.cpp | 5 +- dom/indexedDB/CheckPermissionsHelper.h | 6 +- dom/indexedDB/DatabaseInfo.h | 2 +- dom/indexedDB/IDBDatabase.cpp | 105 +- dom/indexedDB/IDBEvents.cpp | 27 +- dom/indexedDB/IDBEvents.h | 48 +- dom/indexedDB/IDBFactory.cpp | 586 +---------- dom/indexedDB/IDBFactory.h | 4 +- dom/indexedDB/IDBRequest.cpp | 78 +- dom/indexedDB/IDBRequest.h | 41 +- dom/indexedDB/IDBTransaction.cpp | 30 +- dom/indexedDB/IDBTransaction.h | 19 +- dom/indexedDB/IndexedDatabase.h | 2 + dom/indexedDB/IndexedDatabaseManager.cpp | 52 +- dom/indexedDB/IndexedDatabaseManager.h | 5 +- dom/indexedDB/Makefile.in | 3 +- dom/indexedDB/OpenDatabaseHelper.cpp | 981 ++++++++++++++++++ dom/indexedDB/OpenDatabaseHelper.h | 129 +++ dom/indexedDB/nsIIDBDatabase.idl | 8 +- dom/indexedDB/nsIIDBFactory.idl | 8 +- ...ngeRequest.idl => nsIIDBOpenDBRequest.idl} | 11 +- dom/indexedDB/nsIIDBVersionChangeEvent.idl | 5 +- dom/indexedDB/test/bfcache_iframe1.html | 17 +- dom/indexedDB/test/bfcache_iframe2.html | 22 +- dom/indexedDB/test/browser_forgetThisSite.js | 10 +- .../test/browser_forgetThisSiteAdd.html | 16 +- .../test/browser_forgetThisSiteGet.html | 2 +- .../test/browser_permissionsPrompt.html | 2 +- dom/indexedDB/test/browser_quotaPrompt.html | 15 +- .../test/browser_quotaPromptAllow.js | 4 +- dom/indexedDB/test/browser_quotaPromptDeny.js | 4 +- ...rror_events_abort_transactions_iframe.html | 33 +- .../test/event_propagation_iframe.html | 9 +- .../exceptions_in_success_events_iframe.html | 35 +- dom/indexedDB/test/leaving_page_iframe.html | 18 +- .../test/test_add_twice_failure.html | 9 +- .../test/test_autoIncrement_indexes.html | 7 +- dom/indexedDB/test/test_bad_keypath.html | 10 +- dom/indexedDB/test/test_bfcache.html | 2 +- dom/indexedDB/test/test_clear.html | 9 +- dom/indexedDB/test/test_create_index.html | 9 +- .../test_create_index_with_integer_keys.html | 21 +- .../test/test_create_objectStore.html | 13 +- dom/indexedDB/test/test_cursor_mutation.html | 8 +- .../test_cursor_update_updates_indexes.html | 14 +- dom/indexedDB/test/test_cursors.html | 9 +- dom/indexedDB/test/test_event_source.html | 14 +- dom/indexedDB/test/test_getAll.html | 9 +- dom/indexedDB/test/test_global_data.html | 11 +- dom/indexedDB/test/test_index_getAll.html | 9 +- .../test/test_index_getAllObjects.html | 9 +- .../test/test_index_object_cursors.html | 9 +- dom/indexedDB/test/test_indexes.html | 9 +- .../test/test_indexes_bad_values.html | 9 +- dom/indexedDB/test/test_key_requirements.html | 9 +- dom/indexedDB/test/test_leaving_page.html | 2 +- dom/indexedDB/test/test_objectCursors.html | 22 +- ...inline_autoincrement_key_added_on_put.html | 9 +- .../test/test_objectStore_remove_values.html | 17 +- dom/indexedDB/test/test_object_identity.html | 10 +- dom/indexedDB/test/test_odd_result_order.html | 14 +- dom/indexedDB/test/test_open_empty_db.html | 8 +- dom/indexedDB/test/test_open_objectStore.html | 11 +- .../test/test_overlapping_transactions.html | 18 +- dom/indexedDB/test/test_put_get_values.html | 9 +- .../test_put_get_values_autoIncrement.html | 9 +- .../test/test_readonly_transactions.html | 9 +- dom/indexedDB/test/test_remove_index.html | 9 +- .../test/test_remove_objectStore.html | 29 +- .../test/test_request_readyState.html | 13 +- dom/indexedDB/test/test_setVersion.html | 19 +- dom/indexedDB/test/test_setVersion_abort.html | 29 +- .../test/test_setVersion_events.html | 112 +- .../test/test_success_events_after_abort.html | 11 +- .../test/test_transaction_abort.html | 9 +- .../test/test_transaction_lifetimes.html | 7 +- .../test_transaction_lifetimes_nested.html | 7 +- .../test/test_writer_starvation.html | 9 +- dom/indexedDB/test/third_party_iframe2.html | 2 +- js/xpconnect/src/dom_quickstubs.qsconf | 2 +- js/xpconnect/src/qsgen.py | 2 +- 88 files changed, 1776 insertions(+), 1299 deletions(-) create mode 100644 dom/indexedDB/OpenDatabaseHelper.cpp create mode 100644 dom/indexedDB/OpenDatabaseHelper.h rename dom/indexedDB/{nsIIDBVersionChangeRequest.idl => nsIIDBOpenDBRequest.idl} (87%) diff --git a/content/base/src/nsGkAtomList.h b/content/base/src/nsGkAtomList.h index 55906c81d355..f1c856a05fe3 100644 --- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -646,6 +646,7 @@ GK_ATOM(onbeforepaste, "onbeforepaste") GK_ATOM(onbeforeprint, "onbeforeprint") GK_ATOM(onbeforescriptexecute, "onbeforescriptexecute") GK_ATOM(onbeforeunload, "onbeforeunload") +GK_ATOM(onblocked, "onblocked") GK_ATOM(onblur, "onblur") GK_ATOM(onbroadcast, "onbroadcast") GK_ATOM(onchange, "onchange") @@ -741,6 +742,7 @@ GK_ATOM(ontouchcancel, "ontouchcancel") GK_ATOM(ontransitionend, "ontransitionend") GK_ATOM(onunderflow, "onunderflow") GK_ATOM(onunload, "onunload") +GK_ATOM(onupgradeneeded, "onupgradeneeded") GK_ATOM(open, "open") GK_ATOM(optgroup, "optgroup") GK_ATOM(optimum, "optimum") diff --git a/dom/base/domerr.msg b/dom/base/domerr.msg index b9d6231de0af..450e7d459585 100644 --- a/dom/base/domerr.msg +++ b/dom/base/domerr.msg @@ -95,9 +95,11 @@ DOM_MSG_DEF(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR, "A mutation operation was at DOM_MSG_DEF(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR, "A request was placed against a transaction which is currently not active, or which is finished.") DOM_MSG_DEF(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR, "A request was aborted, for example through a call to IDBTransaction.abort.") DOM_MSG_DEF(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR, "A mutation operation was attempted in a READ_ONLY transaction.") -DOM_MSG_DEF(NS_ERROR_DOM_INDEXEDDB_RECOVERABLE_ERR, "The operation failed because the database was prevented from taking an action. The operation might be able to succeed if the application performs some recovery steps and retries the entire transaction. For example, there was not enough remaining storage space, or the storage quota was reached and the user declined to give more space to the database.") -DOM_MSG_DEF(NS_ERROR_DOM_INDEXEDDB_TRANSIENT_ERR, "The operation failed because of some temporary problems. The failed operation might be able to succeed when the operation is retried without any intervention by application-level functionality.") DOM_MSG_DEF(NS_ERROR_DOM_INDEXEDDB_TIMEOUT_ERR, "A lock for the transaction could not be obtained in a reasonable time.") +DOM_MSG_DEF(NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR, "The current transaction exceeded its quota limitations.") +DOM_MSG_DEF(NS_ERROR_DOM_INDEXEDDB_VERSION_ERR, "The operation failed because the stored database is a higher version than the version requested.") + +DOM_MSG_DEF(NS_ERROR_DOM_INDEXEDDB_RECOVERABLE_ERR, "The operation failed because the database was prevented from taking an action. The operation might be able to succeed if the application performs some recovery steps and retries the entire transaction. For example, there was not enough remaining storage space, or the storage quota was reached and the user declined to give more space to the database.") DOM_MSG_DEF(NS_ERROR_DOM_INDEXEDDB_DEADLOCK_ERR, "The current transaction was automatically rolled back by the database because of deadlock or other transaction serialization failures.") /* DOM error codes defined by us */ diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index df67fd021651..30137361550e 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -1505,7 +1505,7 @@ static nsDOMClassInfoData sClassInfoData[] = { DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(IDBVersionChangeEvent, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) - NS_DEFINE_CLASSINFO_DATA(IDBVersionChangeRequest, nsDOMGenericSH, + NS_DEFINE_CLASSINFO_DATA(IDBOpenDBRequest, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(IDBDatabaseException, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) @@ -4087,8 +4087,8 @@ nsDOMClassInfo::Init() DOM_CLASSINFO_MAP_ENTRY(nsIDOMEvent) DOM_CLASSINFO_MAP_END - DOM_CLASSINFO_MAP_BEGIN(IDBVersionChangeRequest, nsIIDBVersionChangeRequest) - DOM_CLASSINFO_MAP_ENTRY(nsIIDBVersionChangeRequest) + DOM_CLASSINFO_MAP_BEGIN(IDBOpenDBRequest, nsIIDBOpenDBRequest) + DOM_CLASSINFO_MAP_ENTRY(nsIIDBOpenDBRequest) DOM_CLASSINFO_MAP_ENTRY(nsIIDBRequest) DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget) DOM_CLASSINFO_MAP_END diff --git a/dom/base/nsDOMClassInfoClasses.h b/dom/base/nsDOMClassInfoClasses.h index 5f532ea5884c..7a2c9008a1f4 100644 --- a/dom/base/nsDOMClassInfoClasses.h +++ b/dom/base/nsDOMClassInfoClasses.h @@ -506,7 +506,7 @@ DOMCI_CLASS(IDBCursorWithValue) DOMCI_CLASS(IDBKeyRange) DOMCI_CLASS(IDBIndex) DOMCI_CLASS(IDBVersionChangeEvent) -DOMCI_CLASS(IDBVersionChangeRequest) +DOMCI_CLASS(IDBOpenDBRequest) DOMCI_CLASS(IDBDatabaseException) DOMCI_CLASS(Touch) diff --git a/dom/base/nsDOMError.h b/dom/base/nsDOMError.h index 2b0ba1351a07..2715b70dd4bb 100644 --- a/dom/base/nsDOMError.h +++ b/dom/base/nsDOMError.h @@ -100,10 +100,12 @@ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_INDEXEDDB,7) #define NS_ERROR_DOM_INDEXEDDB_ABORT_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_INDEXEDDB,8) #define NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_INDEXEDDB,9) -#define NS_ERROR_DOM_INDEXEDDB_RECOVERABLE_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_INDEXEDDB,10) -#define NS_ERROR_DOM_INDEXEDDB_TRANSIENT_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_INDEXEDDB,11) -#define NS_ERROR_DOM_INDEXEDDB_TIMEOUT_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_INDEXEDDB,12) -#define NS_ERROR_DOM_INDEXEDDB_DEADLOCK_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_INDEXEDDB,13) +#define NS_ERROR_DOM_INDEXEDDB_TIMEOUT_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_INDEXEDDB,10) +#define NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_INDEXEDDB,11) +#define NS_ERROR_DOM_INDEXEDDB_VERSION_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_INDEXEDDB,12) + +#define NS_ERROR_DOM_INDEXEDDB_RECOVERABLE_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_INDEXEDDB,1001) +#define NS_ERROR_DOM_INDEXEDDB_DEADLOCK_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_INDEXEDDB,1002) /* DOM error codes defined by us */ diff --git a/dom/indexedDB/AsyncConnectionHelper.cpp b/dom/indexedDB/AsyncConnectionHelper.cpp index 7b9acfa2f551..5136b50a7e9e 100644 --- a/dom/indexedDB/AsyncConnectionHelper.cpp +++ b/dom/indexedDB/AsyncConnectionHelper.cpp @@ -120,10 +120,31 @@ ConvertCloneBuffersToArrayInternal( } // anonymous namespace +nsresult +HelperBase::WrapNative(JSContext* aCx, + nsISupports* aNative, + jsval* aResult) +{ + NS_ASSERTION(aCx, "Null context!"); + NS_ASSERTION(aNative, "Null pointer!"); + NS_ASSERTION(aResult, "Null pointer!"); + NS_ASSERTION(mRequest, "Null request!"); + + JSObject* global = + static_cast(mRequest->ScriptContext()->GetNativeGlobal()); + NS_ENSURE_TRUE(global, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + nsresult rv = + nsContentUtils::WrapNative(aCx, global, aNative, aResult); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + return NS_OK; +} + AsyncConnectionHelper::AsyncConnectionHelper(IDBDatabase* aDatabase, IDBRequest* aRequest) -: mDatabase(aDatabase), - mRequest(aRequest), +: HelperBase(aRequest), + mDatabase(aDatabase), mTimeoutDuration(TimeDuration::FromMilliseconds(kDefaultTimeoutMS)), mResultCode(NS_OK), mDispatched(false) @@ -133,9 +154,9 @@ AsyncConnectionHelper::AsyncConnectionHelper(IDBDatabase* aDatabase, AsyncConnectionHelper::AsyncConnectionHelper(IDBTransaction* aTransaction, IDBRequest* aRequest) -: mDatabase(aTransaction->mDatabase), +: HelperBase(aRequest), + mDatabase(aTransaction->mDatabase), mTransaction(aTransaction), - mRequest(aRequest), mTimeoutDuration(TimeDuration::FromMilliseconds(kDefaultTimeoutMS)), mResultCode(NS_OK), mDispatched(false) @@ -195,7 +216,7 @@ AsyncConnectionHelper::Run() gCurrentTransaction = mTransaction; if (mRequest) { - nsresult rv = mRequest->SetDone(this); + nsresult rv = mRequest->NotifyHelperCompleted(this); if (NS_SUCCEEDED(mResultCode) && NS_FAILED(rv)) { mResultCode = rv; } @@ -379,14 +400,19 @@ AsyncConnectionHelper::Init() return NS_OK; } +already_AddRefed +AsyncConnectionHelper::CreateSuccessEvent() +{ + return CreateGenericEvent(NS_LITERAL_STRING(SUCCESS_EVT_STR)); +} + nsresult AsyncConnectionHelper::OnSuccess() { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); NS_ASSERTION(mRequest, "Null request!"); - nsRefPtr event = - CreateGenericEvent(NS_LITERAL_STRING(SUCCESS_EVT_STR)); + nsRefPtr event = CreateSuccessEvent(); if (!event) { NS_ERROR("Failed to create event!"); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; @@ -468,27 +494,6 @@ AsyncConnectionHelper::ReleaseMainThreadObjects() mRequest = nsnull; } -nsresult -AsyncConnectionHelper::WrapNative(JSContext* aCx, - nsISupports* aNative, - jsval* aResult) -{ - NS_ASSERTION(aCx, "Null context!"); - NS_ASSERTION(aNative, "Null pointer!"); - NS_ASSERTION(aResult, "Null pointer!"); - NS_ASSERTION(mRequest, "Null request!"); - - JSObject* global = - static_cast(mRequest->ScriptContext()->GetNativeGlobal()); - NS_ENSURE_TRUE(global, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - nsresult rv = - nsContentUtils::WrapNative(aCx, global, aNative, aResult); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - return NS_OK; -} - // static nsresult AsyncConnectionHelper::ConvertCloneBuffersToArray( diff --git a/dom/indexedDB/AsyncConnectionHelper.h b/dom/indexedDB/AsyncConnectionHelper.h index 5b5361e14868..6bfba0967557 100644 --- a/dom/indexedDB/AsyncConnectionHelper.h +++ b/dom/indexedDB/AsyncConnectionHelper.h @@ -49,6 +49,8 @@ #include "nsIRunnable.h" #include "nsIThread.h" +#include "nsDOMEvent.h" + #include "mozilla/TimeStamp.h" class mozIStorageConnection; @@ -57,6 +59,33 @@ BEGIN_INDEXEDDB_NAMESPACE class IDBTransaction; +// A common base class for AsyncConnectionHelper and OpenDatabaseHelper that +// IDBRequest can use. +class HelperBase : public nsIRunnable +{ + friend class IDBRequest; +public: + virtual nsresult GetResultCode() = 0; + + virtual nsresult GetSuccessResult(JSContext* aCx, + jsval* aVal) = 0; + +protected: + HelperBase(IDBRequest* aRequest) + : mRequest(aRequest) + { } + + /** + * Helper to wrap a native into a jsval. Uses the global object of the request + * to parent the native. + */ + nsresult WrapNative(JSContext* aCx, + nsISupports* aNative, + jsval* aResult); + + nsRefPtr mRequest; +}; + /** * Must be subclassed. The subclass must implement DoDatabaseWork. It may then * choose to implement OnSuccess and OnError depending on the needs of the @@ -66,11 +95,9 @@ class IDBTransaction; * and Dispatched from the main thread only. Target thread may not be the main * thread. */ -class AsyncConnectionHelper : public nsIRunnable, +class AsyncConnectionHelper : public HelperBase, public mozIStorageProgressHandler { - friend class IDBRequest; - public: NS_DECL_ISUPPORTS NS_DECL_NSIRUNNABLE @@ -128,6 +155,13 @@ protected: */ virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) = 0; + /** + * This function returns the event to be dispatched at the request when + * OnSuccess is called. A subclass can override this to fire an event other + * than "success" at the request. + */ + virtual already_AddRefed CreateSuccessEvent(); + /** * This callback is run on the main thread if DoDatabaseWork returned NS_OK. * The default implementation fires a "success" DOM event with its target set @@ -157,14 +191,6 @@ protected: */ virtual void ReleaseMainThreadObjects(); - /** - * Helper to wrap a native into a jsval. Uses the global object of the request - * to parent the native. - */ - nsresult WrapNative(JSContext* aCx, - nsISupports* aNative, - jsval* aResult); - /** * Helper to make a JS array object out of an array of clone buffers. */ @@ -176,7 +202,6 @@ protected: protected: nsRefPtr mDatabase; nsRefPtr mTransaction; - nsRefPtr mRequest; private: nsCOMPtr mOldProgressHandler; diff --git a/dom/indexedDB/CheckPermissionsHelper.cpp b/dom/indexedDB/CheckPermissionsHelper.cpp index e3564c32ec02..db79222f0e7c 100644 --- a/dom/indexedDB/CheckPermissionsHelper.cpp +++ b/dom/indexedDB/CheckPermissionsHelper.cpp @@ -150,7 +150,7 @@ CheckPermissionsHelper::Run() return NS_OK; } - nsRefPtr helper; + nsRefPtr helper; helper.swap(mHelper); nsCOMPtr window; @@ -168,7 +168,8 @@ CheckPermissionsHelper::Run() "Unknown permission!"); helper->SetError(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); - return helper->Run(); + + return helper->RunImmediately(); } NS_IMETHODIMP diff --git a/dom/indexedDB/CheckPermissionsHelper.h b/dom/indexedDB/CheckPermissionsHelper.h index 60b56197307a..ef0ec219e69d 100644 --- a/dom/indexedDB/CheckPermissionsHelper.h +++ b/dom/indexedDB/CheckPermissionsHelper.h @@ -41,7 +41,7 @@ #define mozilla_dom_indexeddb_checkpermissionshelper_h__ // Only meant to be included in IndexedDB source files, not exported. -#include "AsyncConnectionHelper.h" +#include "OpenDatabaseHelper.h" #include "nsIInterfaceRequestor.h" #include "nsIObserver.h" @@ -62,7 +62,7 @@ public: NS_DECL_NSIINTERFACEREQUESTOR NS_DECL_NSIOBSERVER - CheckPermissionsHelper(AsyncConnectionHelper* aHelper, + CheckPermissionsHelper(OpenDatabaseHelper* aHelper, nsIDOMWindow* aWindow, const nsAString& aName, const nsACString& aASCIIOrigin) @@ -80,7 +80,7 @@ public: } private: - nsRefPtr mHelper; + nsRefPtr mHelper; nsCOMPtr mWindow; nsString mName; nsCString mASCIIOrigin; diff --git a/dom/indexedDB/DatabaseInfo.h b/dom/indexedDB/DatabaseInfo.h index 50a715b325fd..1fdab7e32857 100644 --- a/dom/indexedDB/DatabaseInfo.h +++ b/dom/indexedDB/DatabaseInfo.h @@ -68,7 +68,7 @@ struct DatabaseInfo bool ContainsStoreName(const nsAString& aName); nsString name; - nsString version; + PRUint64 version; PRUint32 id; nsString filePath; PRInt64 nextObjectStoreId; diff --git a/dom/indexedDB/IDBDatabase.cpp b/dom/indexedDB/IDBDatabase.cpp index ffe516ba2221..34c86d40e61d 100644 --- a/dom/indexedDB/IDBDatabase.cpp +++ b/dom/indexedDB/IDBDatabase.cpp @@ -72,24 +72,6 @@ mozilla::Mutex* gPromptHelpersMutex = nsnull; // Protected by gPromptHelpersMutex. nsTArray >* gPromptHelpers = nsnull; -class SetVersionHelper : public AsyncConnectionHelper -{ -public: - SetVersionHelper(IDBTransaction* aTransaction, - IDBRequest* aRequest, - const nsAString& aVersion) - : AsyncConnectionHelper(aTransaction, aRequest), mVersion(aVersion) - { } - - nsresult DoDatabaseWork(mozIStorageConnection* aConnection); - nsresult GetSuccessResult(JSContext* aCx, - jsval* aVal); - -private: - // In-params - nsString mVersion; -}; - class CreateObjectStoreHelper : public AsyncConnectionHelper { public: @@ -502,14 +484,14 @@ IDBDatabase::GetName(nsAString& aName) } NS_IMETHODIMP -IDBDatabase::GetVersion(nsAString& aVersion) +IDBDatabase::GetVersion(PRUint64* aVersion) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); DatabaseInfo* info; if (!DatabaseInfo::Get(mDatabaseId, &info)) { NS_ERROR("This should never fail!"); } - aVersion.Assign(info->version); + *aVersion = info->version; return NS_OK; } @@ -687,48 +669,6 @@ IDBDatabase::DeleteObjectStore(const nsAString& aName) return NS_OK; } -NS_IMETHODIMP -IDBDatabase::SetVersion(const nsAString& aVersion, - JSContext* aCx, - nsIIDBRequest** _retval) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - if (mClosed) { - // XXX Update spec for a real error code here. - return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; - } - - DatabaseInfo* info; - if (!DatabaseInfo::Get(mDatabaseId, &info)) { - NS_ERROR("This should never fail!"); - } - - // Lock the whole database. - nsTArray storesToOpen; - nsRefPtr transaction = - IDBTransaction::Create(this, storesToOpen, IDBTransaction::VERSION_CHANGE, - kDefaultDatabaseTimeoutSeconds, true); - NS_ENSURE_TRUE(transaction, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - nsRefPtr request = - IDBVersionChangeRequest::Create(static_cast(this), - ScriptContext(), Owner(), transaction); - NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - nsRefPtr helper = - new SetVersionHelper(transaction, request, aVersion); - - IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get(); - NS_ASSERTION(mgr, "This should never be null!"); - - nsresult rv = mgr->SetDatabaseVersion(this, request, aVersion, helper); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - request.forget(_retval); - return NS_OK; -} - NS_IMETHODIMP IDBDatabase::Transaction(nsIVariant* aStoreNames, PRUint16 aMode, @@ -933,47 +873,6 @@ IDBDatabase::PostHandleEvent(nsEventChainPostVisitor& aVisitor) return NS_OK; } -nsresult -SetVersionHelper::DoDatabaseWork(mozIStorageConnection* aConnection) -{ - NS_PRECONDITION(aConnection, "Passing a null connection!"); - - nsCOMPtr stmt; - nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( - "UPDATE database " - "SET version = :version" - ), getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - rv = stmt->BindStringByName(NS_LITERAL_CSTRING("version"), mVersion); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (NS_FAILED(stmt->Execute())) { - return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR; - } - - return NS_OK; -} - -nsresult -SetVersionHelper::GetSuccessResult(JSContext* aCx, - jsval* aVal) -{ - DatabaseInfo* info; - if (!DatabaseInfo::Get(mDatabase->Id(), &info)) { - NS_ERROR("This should never fail!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - info->version = mVersion; - - nsresult rv = WrapNative(aCx, NS_ISUPPORTS_CAST(nsIDOMEventTarget*, - mTransaction), - aVal); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - nsresult CreateObjectStoreHelper::DoDatabaseWork(mozIStorageConnection* aConnection) { diff --git a/dom/indexedDB/IDBEvents.cpp b/dom/indexedDB/IDBEvents.cpp index 349d43b95979..55f6f7da1701 100644 --- a/dom/indexedDB/IDBEvents.cpp +++ b/dom/indexedDB/IDBEvents.cpp @@ -103,9 +103,10 @@ mozilla::dom::indexedDB::CreateGenericEventRunnable(const nsAString& aType, } // static -already_AddRefed +already_AddRefed IDBVersionChangeEvent::CreateInternal(const nsAString& aType, - const nsAString& aVersion) + PRUint64 aOldVersion, + PRUint64 aNewVersion) { nsRefPtr event(new IDBVersionChangeEvent()); @@ -115,7 +116,8 @@ IDBVersionChangeEvent::CreateInternal(const nsAString& aType, rv = event->SetTrusted(true); NS_ENSURE_SUCCESS(rv, nsnull); - event->mVersion = aVersion; + event->mOldVersion = aOldVersion; + event->mNewVersion = aNewVersion; nsDOMEvent* result; event.forget(&result); @@ -125,10 +127,12 @@ IDBVersionChangeEvent::CreateInternal(const nsAString& aType, // static already_AddRefed IDBVersionChangeEvent::CreateRunnableInternal(const nsAString& aType, - const nsAString& aVersion, + PRUint64 aOldVersion, + PRUint64 aNewVersion, nsIDOMEventTarget* aTarget) { - nsCOMPtr event = CreateInternal(aType, aVersion); + nsRefPtr event = + CreateInternal(aType, aOldVersion, aNewVersion); NS_ENSURE_TRUE(event, nsnull); nsCOMPtr runnable(new EventFiringRunnable(aTarget, event)); @@ -146,8 +150,17 @@ NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent) DOMCI_DATA(IDBVersionChangeEvent, IDBVersionChangeEvent) NS_IMETHODIMP -IDBVersionChangeEvent::GetVersion(nsAString& aVersion) +IDBVersionChangeEvent::GetOldVersion(PRUint64* aOldVersion) { - aVersion.Assign(mVersion); + NS_ENSURE_ARG_POINTER(aOldVersion); + *aOldVersion = mOldVersion; + return NS_OK; +} + +NS_IMETHODIMP +IDBVersionChangeEvent::GetNewVersion(PRUint64* aNewVersion) +{ + NS_ENSURE_ARG_POINTER(aNewVersion); + *aNewVersion = mNewVersion; return NS_OK; } diff --git a/dom/indexedDB/IDBEvents.h b/dom/indexedDB/IDBEvents.h index 87f003045561..150c7911e743 100644 --- a/dom/indexedDB/IDBEvents.h +++ b/dom/indexedDB/IDBEvents.h @@ -56,6 +56,7 @@ #define TIMEOUT_EVT_STR "timeout" #define VERSIONCHANGE_EVT_STR "versionchange" #define BLOCKED_EVT_STR "blocked" +#define UPGRADENEEDED_EVT_STR "upgradeneeded" BEGIN_INDEXEDDB_NAMESPACE @@ -75,48 +76,65 @@ public: NS_FORWARD_TO_NSDOMEVENT NS_DECL_NSIIDBVERSIONCHANGEEVENT - inline static already_AddRefed - Create(const nsAString& aVersion) + inline static already_AddRefed + Create(PRInt64 aOldVersion, + PRInt64 aNewVersion) { - return CreateInternal(NS_LITERAL_STRING(VERSIONCHANGE_EVT_STR), aVersion); + return CreateInternal(NS_LITERAL_STRING(VERSIONCHANGE_EVT_STR), + aOldVersion, aNewVersion); } - inline static already_AddRefed - CreateBlocked(const nsAString& aVersion) + inline static already_AddRefed + CreateBlocked(PRUint64 aOldVersion, + PRUint64 aNewVersion) { - return CreateInternal(NS_LITERAL_STRING(BLOCKED_EVT_STR), aVersion); + return CreateInternal(NS_LITERAL_STRING(BLOCKED_EVT_STR), + aOldVersion, aNewVersion); + } + + inline static already_AddRefed + CreateUpgradeNeeded(PRUint64 aOldVersion, + PRUint64 aNewVersion) + { + return CreateInternal(NS_LITERAL_STRING(UPGRADENEEDED_EVT_STR), + aOldVersion, aNewVersion); } inline static already_AddRefed - CreateRunnable(const nsAString& aVersion, + CreateRunnable(PRUint64 aOldVersion, + PRUint64 aNewVersion, nsIDOMEventTarget* aTarget) { return CreateRunnableInternal(NS_LITERAL_STRING(VERSIONCHANGE_EVT_STR), - aVersion, aTarget); + aOldVersion, aNewVersion, aTarget); } static already_AddRefed - CreateBlockedRunnable(const nsAString& aVersion, + CreateBlockedRunnable(PRUint64 aOldVersion, + PRUint64 aNewVersion, nsIDOMEventTarget* aTarget) { - return CreateRunnableInternal(NS_LITERAL_STRING(BLOCKED_EVT_STR), aVersion, - aTarget); + return CreateRunnableInternal(NS_LITERAL_STRING(BLOCKED_EVT_STR), + aOldVersion, aNewVersion, aTarget); } protected: IDBVersionChangeEvent() : nsDOMEvent(nsnull, nsnull) { } virtual ~IDBVersionChangeEvent() { } - static already_AddRefed + static already_AddRefed CreateInternal(const nsAString& aType, - const nsAString& aVersion); + PRUint64 aOldVersion, + PRUint64 aNewVersion); static already_AddRefed CreateRunnableInternal(const nsAString& aType, - const nsAString& aVersion, + PRUint64 aOldVersion, + PRUint64 aNewVersion, nsIDOMEventTarget* aTarget); - nsString mVersion; + PRUint64 mOldVersion; + PRUint64 mNewVersion; }; END_INDEXEDDB_NAMESPACE diff --git a/dom/indexedDB/IDBFactory.cpp b/dom/indexedDB/IDBFactory.cpp index c3a82f8844a9..be7eb62c773e 100644 --- a/dom/indexedDB/IDBFactory.cpp +++ b/dom/indexedDB/IDBFactory.cpp @@ -53,7 +53,6 @@ #include "nsDirectoryServiceUtils.h" #include "nsDOMClassInfoID.h" #include "nsIPrincipal.h" -#include "nsEscape.h" #include "nsHashKeys.h" #include "nsPIDOMWindow.h" #include "nsServiceManagerUtils.h" @@ -65,6 +64,7 @@ #include "CheckPermissionsHelper.h" #include "DatabaseInfo.h" #include "IDBDatabase.h" +#include "IDBEvents.h" #include "IDBKeyRange.h" #include "IndexedDatabaseManager.h" #include "LazyIdleThread.h" @@ -72,8 +72,6 @@ using namespace mozilla; -#define DB_SCHEMA_VERSION 4 - USING_INDEXEDDB_NAMESPACE namespace { @@ -89,360 +87,6 @@ struct ObjectStoreInfoMap ObjectStoreInfo* info; }; -class OpenDatabaseHelper : public AsyncConnectionHelper -{ -public: - OpenDatabaseHelper(IDBRequest* aRequest, - const nsAString& aName, - const nsACString& aASCIIOrigin) - : AsyncConnectionHelper(static_cast(nsnull), aRequest), - mName(aName), mASCIIOrigin(aASCIIOrigin), mDatabaseId(0), - mLastObjectStoreId(0), mLastIndexId(0) - { } - - nsresult DoDatabaseWork(mozIStorageConnection* aConnection); - nsresult GetSuccessResult(JSContext* aCx, - jsval* aVal); - -private: - // In-params. - nsString mName; - nsCString mASCIIOrigin; - - // Out-params. - nsTArray > mObjectStores; - nsString mVersion; - PRUint32 mDataVersion; - nsString mDatabaseFilePath; - PRUint32 mDatabaseId; - PRInt64 mLastObjectStoreId; - PRInt64 mLastIndexId; -}; - -nsresult -CreateTables(mozIStorageConnection* aDBConn) -{ - NS_PRECONDITION(!NS_IsMainThread(), - "Creating tables on the main thread!"); - NS_PRECONDITION(aDBConn, "Passing a null database connection!"); - - // Table `database` - nsresult rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE database (" - "name TEXT NOT NULL, " - "version TEXT DEFAULT NULL, " - "dataVersion INTEGER NOT NULL" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - // Table `object_store` - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE object_store (" - "id INTEGER, " - "name TEXT NOT NULL, " - "key_path TEXT NOT NULL, " - "auto_increment INTEGER NOT NULL DEFAULT 0, " - "PRIMARY KEY (id), " - "UNIQUE (name)" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - // Table `object_data` - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE object_data (" - "id INTEGER, " - "object_store_id INTEGER NOT NULL, " - "data BLOB NOT NULL, " - "key_value DEFAULT NULL, " // NONE affinity - "PRIMARY KEY (id), " - "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " - "CASCADE" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE UNIQUE INDEX key_index " - "ON object_data (key_value, object_store_id);" - )); - NS_ENSURE_SUCCESS(rv, rv); - - // Table `ai_object_data` - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE ai_object_data (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "object_store_id INTEGER NOT NULL, " - "data BLOB NOT NULL, " - "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " - "CASCADE" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE UNIQUE INDEX ai_key_index " - "ON ai_object_data (id, object_store_id);" - )); - NS_ENSURE_SUCCESS(rv, rv); - - // Table `index` - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE object_store_index (" - "id INTEGER, " - "object_store_id INTEGER NOT NULL, " - "name TEXT NOT NULL, " - "key_path TEXT NOT NULL, " - "unique_index INTEGER NOT NULL, " - "object_store_autoincrement INTERGER NOT NULL, " - "PRIMARY KEY (id), " - "UNIQUE (object_store_id, name), " - "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " - "CASCADE" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - // Table `index_data` - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE index_data (" - "id INTEGER, " - "index_id INTEGER NOT NULL, " - "object_data_id INTEGER NOT NULL, " - "object_data_key NOT NULL, " // NONE affinity - "value NOT NULL, " - "PRIMARY KEY (id), " - "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " - "CASCADE, " - "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " - "CASCADE" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX value_index " - "ON index_data (index_id, value);" - )); - NS_ENSURE_SUCCESS(rv, rv); - - // Table `unique_index_data` - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE unique_index_data (" - "id INTEGER, " - "index_id INTEGER NOT NULL, " - "object_data_id INTEGER NOT NULL, " - "object_data_key NOT NULL, " // NONE affinity - "value NOT NULL, " - "PRIMARY KEY (id), " - "UNIQUE (index_id, value), " - "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " - "CASCADE " - "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " - "CASCADE" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - // Table `ai_index_data` - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE ai_index_data (" - "id INTEGER, " - "index_id INTEGER NOT NULL, " - "ai_object_data_id INTEGER NOT NULL, " - "value NOT NULL, " - "PRIMARY KEY (id), " - "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " - "CASCADE, " - "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE " - "CASCADE" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX ai_value_index " - "ON ai_index_data (index_id, value);" - )); - NS_ENSURE_SUCCESS(rv, rv); - - // Table `ai_unique_index_data` - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE ai_unique_index_data (" - "id INTEGER, " - "index_id INTEGER NOT NULL, " - "ai_object_data_id INTEGER NOT NULL, " - "value NOT NULL, " - "PRIMARY KEY (id), " - "UNIQUE (index_id, value), " - "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " - "CASCADE, " - "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE " - "CASCADE" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aDBConn->SetSchemaVersion(DB_SCHEMA_VERSION); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -CreateMetaData(mozIStorageConnection* aConnection, - const nsAString& aName) -{ - NS_PRECONDITION(!NS_IsMainThread(), "Wrong thread!"); - NS_PRECONDITION(aConnection, "Null database!"); - - nsCOMPtr stmt; - nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( - "INSERT OR REPLACE INTO database (name, dataVersion) " - "VALUES (:name, :dataVersion)" - ), getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), aName); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("dataVersion"), - JS_STRUCTURED_CLONE_VERSION); - NS_ENSURE_SUCCESS(rv, rv); - - return stmt->Execute(); -} - -nsresult -GetDatabaseFile(const nsACString& aASCIIOrigin, - const nsAString& aName, - nsIFile** aDatabaseFile) -{ - NS_ASSERTION(!aASCIIOrigin.IsEmpty() && !aName.IsEmpty(), "Bad arguments!"); - - nsCOMPtr dbFile; - nsresult rv = IDBFactory::GetDirectory(getter_AddRefs(dbFile)); - NS_ENSURE_SUCCESS(rv, rv); - - NS_ConvertASCIItoUTF16 originSanitized(aASCIIOrigin); - originSanitized.ReplaceChar(":/", '+'); - - rv = dbFile->Append(originSanitized); - NS_ENSURE_SUCCESS(rv, rv); - - nsAutoString filename; - filename.AppendInt(HashString(aName)); - - nsCString escapedName; - if (!NS_Escape(NS_ConvertUTF16toUTF8(aName), escapedName, url_XPAlphas)) { - NS_WARNING("Can't escape database name!"); - return NS_ERROR_UNEXPECTED; - } - - const char* forwardIter = escapedName.BeginReading(); - const char* backwardIter = escapedName.EndReading() - 1; - - nsCString substring; - while (forwardIter <= backwardIter && substring.Length() < 21) { - if (substring.Length() % 2) { - substring.Append(*backwardIter--); - } - else { - substring.Append(*forwardIter++); - } - } - - filename.Append(NS_ConvertASCIItoUTF16(substring)); - filename.AppendLiteral(".sqlite"); - - rv = dbFile->Append(filename); - NS_ENSURE_SUCCESS(rv, rv); - - dbFile.forget(aDatabaseFile); - return NS_OK; -} - -nsresult -CreateDatabaseConnection(const nsAString& aName, - nsIFile* aDBFile, - mozIStorageConnection** aConnection) -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - - nsCOMPtr dbDirectory; - nsresult rv = aDBFile->GetParent(getter_AddRefs(dbDirectory)); - NS_ENSURE_SUCCESS(rv, rv); - - bool exists; - rv = aDBFile->Exists(&exists); - NS_ENSURE_SUCCESS(rv, rv); - - NS_NAMED_LITERAL_CSTRING(quotaVFSName, "quota"); - - nsCOMPtr ss = - do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID); - NS_ENSURE_TRUE(ss, NS_ERROR_FAILURE); - - nsCOMPtr connection; - rv = ss->OpenDatabaseWithVFS(aDBFile, quotaVFSName, - getter_AddRefs(connection)); - if (rv == NS_ERROR_FILE_CORRUPTED) { - // Nuke the database file. The web services can recreate their data. - rv = aDBFile->Remove(false); - NS_ENSURE_SUCCESS(rv, rv); - - exists = false; - - rv = ss->OpenDatabaseWithVFS(aDBFile, quotaVFSName, - getter_AddRefs(connection)); - } - NS_ENSURE_SUCCESS(rv, rv); - - // Check to make sure that the database schema is correct. - PRInt32 schemaVersion; - rv = connection->GetSchemaVersion(&schemaVersion); - NS_ENSURE_SUCCESS(rv, rv); - - if (schemaVersion != DB_SCHEMA_VERSION) { - if (exists) { - // If the connection is not at the right schema version, nuke it. - rv = aDBFile->Remove(false); - NS_ENSURE_SUCCESS(rv, rv); - - rv = ss->OpenDatabaseWithVFS(aDBFile, quotaVFSName, - getter_AddRefs(connection)); - NS_ENSURE_SUCCESS(rv, rv); - } - - mozStorageTransaction transaction(connection, false, - mozIStorageConnection::TRANSACTION_IMMEDIATE); - - rv = CreateTables(connection); - NS_ENSURE_SUCCESS(rv, rv); - - rv = CreateMetaData(connection, aName); - NS_ENSURE_SUCCESS(rv, rv); - - rv = transaction.Commit(); - NS_ENSURE_SUCCESS(rv, rv); - } - - // Check to make sure that the database schema is correct again. - NS_ASSERTION(NS_SUCCEEDED(connection->GetSchemaVersion(&schemaVersion)) && - schemaVersion == DB_SCHEMA_VERSION, - "CreateTables failed!"); - - // Turn on foreign key constraints. - rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "PRAGMA foreign_keys = ON;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - connection.forget(aConnection); - return NS_OK; -} - } // anonymous namespace IDBFactory::IDBFactory() @@ -571,13 +215,12 @@ IDBFactory::GetDirectoryForOrigin(const nsACString& aASCIIOrigin, nsresult IDBFactory::LoadDatabaseInformation(mozIStorageConnection* aConnection, PRUint32 aDatabaseId, - nsAString& aVersion, + PRUint64* aVersion, ObjectStoreInfoArray& aObjectStores) { NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); NS_ASSERTION(aConnection, "Null pointer!"); - aVersion.Truncate(); aObjectStores.Clear(); // Load object store names and ids. @@ -672,21 +315,18 @@ IDBFactory::LoadDatabaseInformation(mozIStorageConnection* aConnection, return NS_ERROR_UNEXPECTED; } - nsString version; - rv = stmt->GetString(0, version); - NS_ENSURE_SUCCESS(rv, rv); + PRInt64 version = 0; + rv = stmt->GetInt64(0, &version); - if (version.IsVoid()) { - version.SetIsVoid(false); - } - aVersion = version; - return NS_OK; + *aVersion = NS_MAX(version, 0); + + return rv; } // static nsresult IDBFactory::UpdateDatabaseMetadata(DatabaseInfo* aDatabaseInfo, - const nsAString& aVersion, + PRUint64 aVersion, ObjectStoreInfoArray& aObjectStores) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); @@ -739,11 +379,16 @@ DOMCI_DATA(IDBFactory, IDBFactory) NS_IMETHODIMP IDBFactory::Open(const nsAString& aName, + PRInt64 aVersion, JSContext* aCx, - nsIIDBRequest** _retval) + nsIIDBOpenDBRequest** _retval) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + if (aVersion < 1) { + return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR; + } + if (XRE_GetProcessType() == GeckoProcessType_Content) { // Force ContentChild to cache the path from the parent, so that // we do not end up in a side thread that asks for the path (which @@ -784,12 +429,12 @@ IDBFactory::Open(const nsAString& aName, } } - nsRefPtr request = IDBRequest::Create(this, context, window, - nsnull); + nsRefPtr request = + IDBOpenDBRequest::Create(context, window); NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); nsRefPtr openHelper = - new OpenDatabaseHelper(request, aName, origin); + new OpenDatabaseHelper(request, aName, origin, aVersion); nsRefPtr permissionHelper = new CheckPermissionsHelper(openHelper, window, aName, origin); @@ -803,200 +448,3 @@ IDBFactory::Open(const nsAString& aName, request.forget(_retval); return NS_OK; } - -nsresult -OpenDatabaseHelper::DoDatabaseWork(mozIStorageConnection* aConnection) -{ -#ifdef DEBUG - { - bool correctThread; - NS_ASSERTION(NS_SUCCEEDED(IndexedDatabaseManager::Get()->IOThread()-> - IsOnCurrentThread(&correctThread)) && - correctThread, - "Running on the wrong thread!"); - } -#endif - NS_ASSERTION(!aConnection, "Huh?!"); - - if (IndexedDatabaseManager::IsShuttingDown()) { - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - nsCOMPtr dbFile; - nsresult rv = GetDatabaseFile(mASCIIOrigin, mName, getter_AddRefs(dbFile)); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - rv = dbFile->GetPath(mDatabaseFilePath); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - nsCOMPtr dbDirectory; - rv = dbFile->GetParent(getter_AddRefs(dbDirectory)); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - bool exists; - rv = dbDirectory->Exists(&exists); - NS_ENSURE_SUCCESS(rv, rv); - - if (exists) { - bool isDirectory; - rv = dbDirectory->IsDirectory(&isDirectory); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - NS_ENSURE_TRUE(isDirectory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - } - else { - rv = dbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - } - - IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get(); - NS_ASSERTION(mgr, "This should never be null!"); - - rv = mgr->EnsureQuotaManagementForDirectory(dbDirectory); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - nsCOMPtr connection; - rv = CreateDatabaseConnection(mName, dbFile, getter_AddRefs(connection)); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - // Get the data version. - nsCOMPtr stmt; - rv = connection->CreateStatement(NS_LITERAL_CSTRING( - "SELECT dataVersion " - "FROM database" - ), getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - bool hasResult; - rv = stmt->ExecuteStep(&hasResult); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (!hasResult) { - NS_ERROR("Database has no dataVersion!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - PRInt64 dataVersion; - rv = stmt->GetInt64(0, &dataVersion); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (dataVersion > JS_STRUCTURED_CLONE_VERSION) { - NS_ERROR("Bad data version!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - if (dataVersion < JS_STRUCTURED_CLONE_VERSION) { - // Need to upgrade the database, here, before returning to the main thread. - NS_NOTYETIMPLEMENTED("Implement me!"); - } - - mDatabaseId = HashString(mDatabaseFilePath); - NS_ASSERTION(mDatabaseId, "HashString gave us 0?!"); - - rv = IDBFactory::LoadDatabaseInformation(connection, mDatabaseId, mVersion, - mObjectStores); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - for (PRUint32 i = 0; i < mObjectStores.Length(); i++) { - nsAutoPtr& objectStoreInfo = mObjectStores[i]; - for (PRUint32 j = 0; j < objectStoreInfo->indexes.Length(); j++) { - IndexInfo& indexInfo = objectStoreInfo->indexes[j]; - mLastIndexId = NS_MAX(indexInfo.id, mLastIndexId); - } - mLastObjectStoreId = NS_MAX(objectStoreInfo->id, mLastObjectStoreId); - } - - return NS_OK; -} - -nsresult -OpenDatabaseHelper::GetSuccessResult(JSContext* aCx, - jsval *aVal) -{ - DatabaseInfo* dbInfo; - if (DatabaseInfo::Get(mDatabaseId, &dbInfo)) { - NS_ASSERTION(dbInfo->referenceCount, "Bad reference count!"); - ++dbInfo->referenceCount; - -#ifdef DEBUG - { - NS_ASSERTION(dbInfo->name == mName && - dbInfo->version == mVersion && - dbInfo->id == mDatabaseId && - dbInfo->filePath == mDatabaseFilePath, - "Metadata mismatch!"); - - PRUint32 objectStoreCount = mObjectStores.Length(); - for (PRUint32 index = 0; index < objectStoreCount; index++) { - nsAutoPtr& info = mObjectStores[index]; - NS_ASSERTION(info->databaseId == mDatabaseId, "Huh?!"); - - ObjectStoreInfo* otherInfo; - NS_ASSERTION(ObjectStoreInfo::Get(mDatabaseId, info->name, &otherInfo), - "ObjectStore not known!"); - - NS_ASSERTION(info->name == otherInfo->name && - info->id == otherInfo->id && - info->keyPath == otherInfo->keyPath && - info->autoIncrement == otherInfo->autoIncrement && - info->databaseId == otherInfo->databaseId, - "Metadata mismatch!"); - NS_ASSERTION(dbInfo->ContainsStoreName(info->name), - "Object store names out of date!"); - NS_ASSERTION(info->indexes.Length() == otherInfo->indexes.Length(), - "Bad index length!"); - - PRUint32 indexCount = info->indexes.Length(); - for (PRUint32 indexIndex = 0; indexIndex < indexCount; indexIndex++) { - const IndexInfo& indexInfo = info->indexes[indexIndex]; - const IndexInfo& otherIndexInfo = otherInfo->indexes[indexIndex]; - NS_ASSERTION(indexInfo.id == otherIndexInfo.id, - "Bad index id!"); - NS_ASSERTION(indexInfo.name == otherIndexInfo.name, - "Bad index name!"); - NS_ASSERTION(indexInfo.keyPath == otherIndexInfo.keyPath, - "Bad index keyPath!"); - NS_ASSERTION(indexInfo.unique == otherIndexInfo.unique, - "Bad index unique value!"); - NS_ASSERTION(indexInfo.autoIncrement == otherIndexInfo.autoIncrement, - "Bad index autoIncrement value!"); - } - } - } -#endif - - } - else { - nsAutoPtr newInfo(new DatabaseInfo()); - - newInfo->name = mName; - newInfo->id = mDatabaseId; - newInfo->filePath = mDatabaseFilePath; - newInfo->referenceCount = 1; - - if (!DatabaseInfo::Put(newInfo)) { - NS_ERROR("Failed to add to hash!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - dbInfo = newInfo.forget(); - - nsresult rv = IDBFactory::UpdateDatabaseMetadata(dbInfo, mVersion, - mObjectStores); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - NS_ASSERTION(mObjectStores.IsEmpty(), "Should have swapped!"); - } - - dbInfo->nextObjectStoreId = mLastObjectStoreId + 1; - dbInfo->nextIndexId = mLastIndexId + 1; - - nsRefPtr database = - IDBDatabase::Create(mRequest->ScriptContext(), mRequest->Owner(), dbInfo, - mASCIIOrigin); - if (!database) { - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - return WrapNative(aCx, NS_ISUPPORTS_CAST(nsIDOMEventTarget*, database), - aVal); -} diff --git a/dom/indexedDB/IDBFactory.h b/dom/indexedDB/IDBFactory.h index 3c0d46b1daf7..786c621a1783 100644 --- a/dom/indexedDB/IDBFactory.h +++ b/dom/indexedDB/IDBFactory.h @@ -85,12 +85,12 @@ public: static nsresult LoadDatabaseInformation(mozIStorageConnection* aConnection, PRUint32 aDatabaseId, - nsAString& aVersion, + PRUint64* aVersion, ObjectStoreInfoArray& aObjectStores); static nsresult UpdateDatabaseMetadata(DatabaseInfo* aDatabaseInfo, - const nsAString& aVersion, + PRUint64 aVersion, ObjectStoreInfoArray& aObjectStores); private: diff --git a/dom/indexedDB/IDBRequest.cpp b/dom/indexedDB/IDBRequest.cpp index dce02a60e76a..04d1f883f0ee 100644 --- a/dom/indexedDB/IDBRequest.cpp +++ b/dom/indexedDB/IDBRequest.cpp @@ -121,7 +121,7 @@ IDBRequest::Reset() } nsresult -IDBRequest::SetDone(AsyncConnectionHelper* aHelper) +IDBRequest::NotifyHelperCompleted(HelperBase* aHelper) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); NS_ASSERTION(!mHaveResultOrErrorCode, "Already called!"); @@ -203,12 +203,10 @@ IDBRequest::GetReadyState(PRUint16* aReadyState) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - if (mHaveResultOrErrorCode) { - *aReadyState = nsIIDBRequest::DONE; - } - else { - *aReadyState = nsIIDBRequest::LOADING; - } + *aReadyState = mHaveResultOrErrorCode ? + nsIIDBRequest::DONE : + nsIIDBRequest::LOADING; + return NS_OK; } @@ -345,7 +343,7 @@ IDBRequest::PreHandleEvent(nsEventChainPreVisitor& aVisitor) return NS_OK; } -IDBVersionChangeRequest::~IDBVersionChangeRequest() +IDBOpenDBRequest::~IDBOpenDBRequest() { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); @@ -355,11 +353,9 @@ IDBVersionChangeRequest::~IDBVersionChangeRequest() } // static -already_AddRefed -IDBVersionChangeRequest::Create(nsISupports* aSource, - nsIScriptContext* aScriptContext, - nsPIDOMWindow* aOwner, - IDBTransaction* aTransaction) +already_AddRefed +IDBOpenDBRequest::Create(nsIScriptContext* aScriptContext, + nsPIDOMWindow* aOwner) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); @@ -368,10 +364,8 @@ IDBVersionChangeRequest::Create(nsISupports* aSource, return nsnull; } - nsRefPtr request(new IDBVersionChangeRequest()); + nsRefPtr request(new IDBOpenDBRequest()); - request->mSource = aSource; - request->mTransaction = aTransaction; request->mScriptContext = aScriptContext; request->mOwner = aOwner; @@ -379,52 +373,50 @@ IDBVersionChangeRequest::Create(nsISupports* aSource, } void -IDBVersionChangeRequest::RootResultVal() +IDBOpenDBRequest::SetTransaction(IDBTransaction* aTransaction) +{ + mTransaction = aTransaction; +} + +void +IDBOpenDBRequest::RootResultVal() { NS_ASSERTION(!mResultValRooted, "This should be false!"); - NS_HOLD_JS_OBJECTS(this, IDBVersionChangeRequest); + NS_HOLD_JS_OBJECTS(this, IDBOpenDBRequest); mResultValRooted = true; } void -IDBVersionChangeRequest::UnrootResultVal() +IDBOpenDBRequest::UnrootResultVal() { NS_ASSERTION(mResultValRooted, "This should be true!"); - NS_DROP_JS_OBJECTS(this, IDBVersionChangeRequest); + NS_DROP_JS_OBJECTS(this, IDBOpenDBRequest); mResultValRooted = false; } -NS_IMETHODIMP -IDBVersionChangeRequest::SetOnblocked(nsIDOMEventListener* aBlockedListener) -{ - return RemoveAddEventListener(NS_LITERAL_STRING(BLOCKED_EVT_STR), - mOnBlockedListener, aBlockedListener); -} +NS_IMPL_EVENT_HANDLER(IDBOpenDBRequest, blocked) +NS_IMPL_EVENT_HANDLER(IDBOpenDBRequest, upgradeneeded) -NS_IMETHODIMP -IDBVersionChangeRequest::GetOnblocked(nsIDOMEventListener** aBlockedListener) -{ - return GetInnerEventListener(mOnBlockedListener, aBlockedListener); -} +NS_IMPL_CYCLE_COLLECTION_CLASS(IDBOpenDBRequest) -NS_IMPL_CYCLE_COLLECTION_CLASS(IDBVersionChangeRequest) - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBVersionChangeRequest, +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBOpenDBRequest, IDBRequest) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnBlockedListener) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnupgradeneededListener) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnblockedListener) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBVersionChangeRequest, +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBOpenDBRequest, IDBRequest) - NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnBlockedListener) + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnupgradeneededListener) + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnblockedListener) NS_IMPL_CYCLE_COLLECTION_UNLINK_END -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBVersionChangeRequest) - NS_INTERFACE_MAP_ENTRY(nsIIDBVersionChangeRequest) - NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBVersionChangeRequest) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBOpenDBRequest) + NS_INTERFACE_MAP_ENTRY(nsIIDBOpenDBRequest) + NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBOpenDBRequest) NS_INTERFACE_MAP_END_INHERITING(IDBRequest) -NS_IMPL_ADDREF_INHERITED(IDBVersionChangeRequest, IDBRequest) -NS_IMPL_RELEASE_INHERITED(IDBVersionChangeRequest, IDBRequest) +NS_IMPL_ADDREF_INHERITED(IDBOpenDBRequest, IDBRequest) +NS_IMPL_RELEASE_INHERITED(IDBOpenDBRequest, IDBRequest) -DOMCI_DATA(IDBVersionChangeRequest, IDBVersionChangeRequest) +DOMCI_DATA(IDBOpenDBRequest, IDBOpenDBRequest) diff --git a/dom/indexedDB/IDBRequest.h b/dom/indexedDB/IDBRequest.h index 5bd0a3543702..1cf91f5f4134 100644 --- a/dom/indexedDB/IDBRequest.h +++ b/dom/indexedDB/IDBRequest.h @@ -44,9 +44,9 @@ #include "mozilla/dom/indexedDB/IndexedDatabase.h" #include "nsIIDBRequest.h" -#include "nsIIDBVersionChangeRequest.h" +#include "nsIIDBOpenDBRequest.h" -#include "nsDOMEventTargetHelper.h" +#include "nsDOMEventTargetWrapperCache.h" #include "nsCycleCollectionParticipant.h" class nsIScriptContext; @@ -54,7 +54,7 @@ class nsPIDOMWindow; BEGIN_INDEXEDDB_NAMESPACE -class AsyncConnectionHelper; +class HelperBase; class IDBTransaction; class IDBRequest : public nsDOMEventTargetHelper, @@ -82,7 +82,15 @@ public: void Reset(); - nsresult SetDone(AsyncConnectionHelper* aHelper); + nsresult NotifyHelperCompleted(HelperBase* aHelper); + + void SetError(nsresult rv) + { + NS_ASSERTION(NS_FAILED(rv), "Er, what?"); + NS_ASSERTION(mErrorCode == NS_OK, "Already have an error?"); + + mErrorCode = rv; + } nsIScriptContext* ScriptContext() { @@ -116,30 +124,31 @@ protected: bool mHaveResultOrErrorCode; }; -class IDBVersionChangeRequest : public IDBRequest, - public nsIIDBVersionChangeRequest +class IDBOpenDBRequest : public IDBRequest, + public nsIIDBOpenDBRequest { public: NS_DECL_ISUPPORTS_INHERITED NS_FORWARD_NSIIDBREQUEST(IDBRequest::) - NS_DECL_NSIIDBVERSIONCHANGEREQUEST - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBVersionChangeRequest, + NS_DECL_NSIIDBOPENDBREQUEST + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBOpenDBRequest, IDBRequest) - ~IDBVersionChangeRequest(); - static - already_AddRefed - Create(nsISupports* aSource, - nsIScriptContext* aScriptContext, - nsPIDOMWindow* aOwner, - IDBTransaction* aTransaction); + already_AddRefed + Create(nsIScriptContext* aScriptContext, + nsPIDOMWindow* aOwner); + + void SetTransaction(IDBTransaction* aTransaction); virtual void RootResultVal(); virtual void UnrootResultVal(); protected: - nsRefPtr mOnBlockedListener; + ~IDBOpenDBRequest(); + + nsRefPtr mOnblockedListener; + nsRefPtr mOnupgradeneededListener; }; END_INDEXEDDB_NAMESPACE diff --git a/dom/indexedDB/IDBTransaction.cpp b/dom/indexedDB/IDBTransaction.cpp index 3e4dc5901528..f44037397a3d 100644 --- a/dom/indexedDB/IDBTransaction.cpp +++ b/dom/indexedDB/IDBTransaction.cpp @@ -182,6 +182,14 @@ IDBTransaction::OnRequestFinished() } } +void +IDBTransaction::SetTransactionListener(IDBTransactionListener* aListener) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!mListener, "Shouldn't already have a listener!"); + mListener = aListener; +} + nsresult IDBTransaction::CommitOrRollback() { @@ -190,7 +198,7 @@ IDBTransaction::CommitOrRollback() TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate(); NS_ENSURE_STATE(pool); - nsRefPtr helper(new CommitHelper(this)); + nsRefPtr helper(new CommitHelper(this, mListener)); mCachedStatements.Enumerate(DoomCachedStatements, helper); NS_ASSERTION(!mCachedStatements.Count(), "Statements left!"); @@ -775,8 +783,13 @@ IDBTransaction::Abort() mAborted = true; mReadyState = nsIIDBTransaction::DONE; + if (Mode() == nsIIDBTransaction::VERSION_CHANGE) { + // If a version change transaction is aborted, the db must be closed + mDatabase->Close(); + } + // Fire the abort event if there are no outstanding requests. Otherwise the - // abort event will be fired when all outdtanding requests finish. + // abort event will be fired when all outstanding requests finish. if (needToCommitOrRollback) { return CommitOrRollback(); } @@ -908,8 +921,10 @@ IDBTransaction::AfterProcessNextEvent(nsIThreadInternal* aThread, return NS_OK; } -CommitHelper::CommitHelper(IDBTransaction* aTransaction) +CommitHelper::CommitHelper(IDBTransaction* aTransaction, + IDBTransactionListener* aListener) : mTransaction(aTransaction), + mListener(aListener), mAborted(!!aTransaction->mAborted), mHaveMetadata(false) { @@ -965,7 +980,14 @@ CommitHelper::Run() #ifdef DEBUG mTransaction->mFiredCompleteOrAbort = true; #endif + + // Tell the listener (if we have one) that we're done + if (mListener) { + mListener->NotifyTransactionComplete(mTransaction); + } + mTransaction = nsnull; + return NS_OK; } @@ -994,7 +1016,7 @@ CommitHelper::Run() nsresult rv = IDBFactory::LoadDatabaseInformation(mConnection, mTransaction->Database()->Id(), - mOldVersion, mOldObjectStores); + &mOldVersion, mOldObjectStores); if (NS_SUCCEEDED(rv)) { mHaveMetadata = true; } diff --git a/dom/indexedDB/IDBTransaction.h b/dom/indexedDB/IDBTransaction.h index ff59ca1615d9..d9e998a8c0d4 100644 --- a/dom/indexedDB/IDBTransaction.h +++ b/dom/indexedDB/IDBTransaction.h @@ -65,6 +65,15 @@ class CommitHelper; struct ObjectStoreInfo; class TransactionThreadPool; +class IDBTransactionListener +{ +public: + NS_IMETHOD_(nsrefcnt) AddRef() = 0; + NS_IMETHOD_(nsrefcnt) Release() = 0; + + virtual nsresult NotifyTransactionComplete(IDBTransaction* aTransaction) = 0; +}; + class IDBTransaction : public nsDOMEventTargetHelper, public nsIIDBTransaction, public nsIThreadObserver @@ -95,6 +104,8 @@ public: void OnNewRequest(); void OnRequestFinished(); + void SetTransactionListener(IDBTransactionListener* aListener); + bool StartSavepoint(); nsresult ReleaseSavepoint(); void RollbackSavepoint(); @@ -189,6 +200,8 @@ private: nsInterfaceHashtable mCachedStatements; + nsRefPtr mListener; + // Only touched on the database thread. nsCOMPtr mConnection; @@ -211,7 +224,8 @@ public: NS_DECL_ISUPPORTS NS_DECL_NSIRUNNABLE - CommitHelper(IDBTransaction* aTransaction); + CommitHelper(IDBTransaction* aTransaction, + IDBTransactionListener* aListener); ~CommitHelper(); template @@ -229,10 +243,11 @@ public: private: nsRefPtr mTransaction; + nsRefPtr mListener; nsCOMPtr mConnection; nsAutoTArray, 10> mDoomedObjects; - nsString mOldVersion; + PRUint64 mOldVersion; nsTArray > mOldObjectStores; bool mAborted; diff --git a/dom/indexedDB/IndexedDatabase.h b/dom/indexedDB/IndexedDatabase.h index 59f1eaf50582..db956429a156 100644 --- a/dom/indexedDB/IndexedDatabase.h +++ b/dom/indexedDB/IndexedDatabase.h @@ -51,6 +51,8 @@ #include "nsStringGlue.h" #include "nsTArray.h" +#define DB_SCHEMA_VERSION 5 + #define BEGIN_INDEXEDDB_NAMESPACE \ namespace mozilla { namespace dom { namespace indexedDB { diff --git a/dom/indexedDB/IndexedDatabaseManager.cpp b/dom/indexedDB/IndexedDatabaseManager.cpp index e5d75092a490..e4265bbbfd0f 100644 --- a/dom/indexedDB/IndexedDatabaseManager.cpp +++ b/dom/indexedDB/IndexedDatabaseManager.cpp @@ -149,14 +149,20 @@ class DelayedSetVersion : public nsRunnable { public: DelayedSetVersion(IDBDatabase* aDatabase, - IDBVersionChangeRequest* aRequest, - const nsAString& aVersion, + IDBOpenDBRequest* aRequest, + PRInt64 aOldVersion, + PRInt64 aNewVersion, AsyncConnectionHelper* aHelper) : mDatabase(aDatabase), mRequest(aRequest), - mVersion(aVersion), + mOldVersion(aOldVersion), + mNewVersion(aNewVersion), mHelper(aHelper) - { } + { + NS_ASSERTION(aDatabase, "Null database!"); + NS_ASSERTION(aRequest, "Null request!"); + NS_ASSERTION(aHelper, "Null helper!"); + } NS_IMETHOD Run() { @@ -165,7 +171,8 @@ public: IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get(); NS_ASSERTION(mgr, "This should never be null!"); - nsresult rv = mgr->SetDatabaseVersion(mDatabase, mRequest, mVersion, + nsresult rv = mgr->SetDatabaseVersion(mDatabase, mRequest, + mOldVersion, mNewVersion, mHelper); NS_ENSURE_SUCCESS(rv, rv); @@ -174,8 +181,9 @@ public: private: nsRefPtr mDatabase; - nsRefPtr mRequest; - nsString mVersion; + nsRefPtr mRequest; + PRInt64 mOldVersion; + PRInt64 mNewVersion; nsRefPtr mHelper; }; @@ -187,12 +195,14 @@ class VersionChangeEventsRunnable : public nsRunnable public: VersionChangeEventsRunnable( IDBDatabase* aRequestingDatabase, - IDBVersionChangeRequest* aRequest, + IDBOpenDBRequest* aRequest, nsTArray >& aWaitingDatabases, - const nsAString& aVersion) + PRInt64 aOldVersion, + PRInt64 aNewVersion) : mRequestingDatabase(aRequestingDatabase), mRequest(aRequest), - mVersion(aVersion) + mOldVersion(aOldVersion), + mNewVersion(aNewVersion) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); NS_ASSERTION(aRequestingDatabase, "Null pointer!"); @@ -230,7 +240,8 @@ public: } // Otherwise fire a versionchange event. - nsCOMPtr event(IDBVersionChangeEvent::Create(mVersion)); + nsRefPtr event = + IDBVersionChangeEvent::Create(mOldVersion, mNewVersion); NS_ENSURE_TRUE(event, NS_ERROR_FAILURE); bool dummy; @@ -241,8 +252,8 @@ public: // then fire the blocked event. for (PRUint32 index = 0; index < mWaitingDatabases.Length(); index++) { if (!mWaitingDatabases[index]->IsClosed()) { - nsCOMPtr event = - IDBVersionChangeEvent::CreateBlocked(mVersion); + nsRefPtr event = + IDBVersionChangeEvent::CreateBlocked(mOldVersion, mNewVersion); NS_ENSURE_TRUE(event, NS_ERROR_FAILURE); bool dummy; @@ -257,9 +268,10 @@ public: private: nsRefPtr mRequestingDatabase; - nsRefPtr mRequest; + nsRefPtr mRequest; nsTArray > mWaitingDatabases; - nsString mVersion; + PRInt64 mOldVersion; + PRInt64 mNewVersion; }; } // anonymous namespace @@ -512,8 +524,9 @@ IndexedDatabaseManager::IsShuttingDown() nsresult IndexedDatabaseManager::SetDatabaseVersion(IDBDatabase* aDatabase, - IDBVersionChangeRequest* aRequest, - const nsAString& aVersion, + IDBOpenDBRequest* aRequest, + PRInt64 aOldVersion, + PRInt64 aNewVersion, AsyncConnectionHelper* aHelper) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); @@ -530,7 +543,8 @@ IndexedDatabaseManager::SetDatabaseVersion(IDBDatabase* aDatabase, // Same database, just queue this call to run after the current // SetVersion transaction completes. nsRefPtr delayed = - new DelayedSetVersion(aDatabase, aRequest, aVersion, aHelper); + new DelayedSetVersion(aDatabase, aRequest, aOldVersion, aNewVersion, + aHelper); if (!runnable->mDelayedRunnables.AppendElement(delayed)) { NS_WARNING("Out of memory!"); return NS_ERROR_OUT_OF_MEMORY; @@ -600,7 +614,7 @@ IndexedDatabaseManager::SetDatabaseVersion(IDBDatabase* aDatabase, nsRefPtr eventsRunnable = new VersionChangeEventsRunnable(aDatabase, aRequest, waitingDatabases, - aVersion); + aOldVersion, aNewVersion); rv = NS_DispatchToCurrentThread(eventsRunnable); NS_ENSURE_SUCCESS(rv, rv); diff --git a/dom/indexedDB/IndexedDatabaseManager.h b/dom/indexedDB/IndexedDatabaseManager.h index c8d63fd7601e..853d239482a5 100644 --- a/dom/indexedDB/IndexedDatabaseManager.h +++ b/dom/indexedDB/IndexedDatabaseManager.h @@ -97,8 +97,9 @@ public: // Begins the process of setting a database version. nsresult SetDatabaseVersion(IDBDatabase* aDatabase, - IDBVersionChangeRequest* aRequest, - const nsAString& aVersion, + IDBOpenDBRequest* aRequest, + PRInt64 aOldVersion, + PRInt64 aNewVersion, AsyncConnectionHelper* aHelper); // Called when a window is being purged from the bfcache or the user leaves diff --git a/dom/indexedDB/Makefile.in b/dom/indexedDB/Makefile.in index 6b93bd27b7fe..2401e7c853a6 100644 --- a/dom/indexedDB/Makefile.in +++ b/dom/indexedDB/Makefile.in @@ -67,6 +67,7 @@ CPPSRCS = \ IDBFactory.cpp \ IndexedDatabaseManager.cpp \ LazyIdleThread.cpp \ + OpenDatabaseHelper.cpp \ TransactionThreadPool.cpp \ $(NULL) @@ -110,7 +111,7 @@ XPIDLSRCS = \ nsIIDBRequest.idl \ nsIIDBTransaction.idl \ nsIIDBVersionChangeEvent.idl \ - nsIIDBVersionChangeRequest.idl \ + nsIIDBOpenDBRequest.idl \ nsIIndexedDatabaseManager.idl \ $(NULL) diff --git a/dom/indexedDB/OpenDatabaseHelper.cpp b/dom/indexedDB/OpenDatabaseHelper.cpp new file mode 100644 index 000000000000..29c94ca1112a --- /dev/null +++ b/dom/indexedDB/OpenDatabaseHelper.cpp @@ -0,0 +1,981 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Indexed Database. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Ben Turner + * Kyle Huey + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "OpenDatabaseHelper.h" +#include "IDBEvents.h" +#include "IDBFactory.h" +#include "IndexedDatabaseManager.h" + +#include "mozilla/storage.h" +#include "nsIFile.h" + +#include "nsContentUtils.h" +#include "nsEscape.h" +#include "nsThreadUtils.h" + +USING_INDEXEDDB_NAMESPACE + +const extern PRUint32 kDefaultDatabaseTimeoutSeconds = 30; + +namespace { + +nsresult +GetDatabaseFile(const nsACString& aASCIIOrigin, + const nsAString& aName, + nsIFile** aDatabaseFile) +{ + NS_ASSERTION(!aASCIIOrigin.IsEmpty() && !aName.IsEmpty(), "Bad arguments!"); + + nsCOMPtr dbFile; + nsresult rv = IDBFactory::GetDirectory(getter_AddRefs(dbFile)); + NS_ENSURE_SUCCESS(rv, rv); + + NS_ConvertASCIItoUTF16 originSanitized(aASCIIOrigin); + originSanitized.ReplaceChar(":/", '+'); + + rv = dbFile->Append(originSanitized); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoString filename; + filename.AppendInt(HashString(aName)); + + nsCString escapedName; + if (!NS_Escape(NS_ConvertUTF16toUTF8(aName), escapedName, url_XPAlphas)) { + NS_WARNING("Can't escape database name!"); + return NS_ERROR_UNEXPECTED; + } + + const char* forwardIter = escapedName.BeginReading(); + const char* backwardIter = escapedName.EndReading() - 1; + + nsCString substring; + while (forwardIter <= backwardIter && substring.Length() < 21) { + if (substring.Length() % 2) { + substring.Append(*backwardIter--); + } + else { + substring.Append(*forwardIter++); + } + } + + filename.Append(NS_ConvertASCIItoUTF16(substring)); + filename.AppendLiteral(".sqlite"); + + rv = dbFile->Append(filename); + NS_ENSURE_SUCCESS(rv, rv); + + dbFile.forget(aDatabaseFile); + return NS_OK; +} + +nsresult +CreateTables(mozIStorageConnection* aDBConn) +{ + NS_PRECONDITION(!NS_IsMainThread(), + "Creating tables on the main thread!"); + NS_PRECONDITION(aDBConn, "Passing a null database connection!"); + + // Table `database` + nsresult rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE database (" + "name TEXT NOT NULL, " + "version INTEGER NOT NULL DEFAULT 0, " + "dataVersion INTEGER NOT NULL" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + // Table `object_store` + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE object_store (" + "id INTEGER, " + "name TEXT NOT NULL, " + "key_path TEXT NOT NULL, " + "auto_increment INTEGER NOT NULL DEFAULT 0, " + "PRIMARY KEY (id), " + "UNIQUE (name)" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + // Table `object_data` + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE object_data (" + "id INTEGER, " + "object_store_id INTEGER NOT NULL, " + "data BLOB NOT NULL, " + "key_value DEFAULT NULL, " // NONE affinity + "PRIMARY KEY (id), " + "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " + "CASCADE" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE UNIQUE INDEX key_index " + "ON object_data (key_value, object_store_id);" + )); + NS_ENSURE_SUCCESS(rv, rv); + + // Table `ai_object_data` + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE ai_object_data (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "object_store_id INTEGER NOT NULL, " + "data BLOB NOT NULL, " + "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " + "CASCADE" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE UNIQUE INDEX ai_key_index " + "ON ai_object_data (id, object_store_id);" + )); + NS_ENSURE_SUCCESS(rv, rv); + + // Table `index` + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE object_store_index (" + "id INTEGER, " + "object_store_id INTEGER NOT NULL, " + "name TEXT NOT NULL, " + "key_path TEXT NOT NULL, " + "unique_index INTEGER NOT NULL, " + "object_store_autoincrement INTERGER NOT NULL, " + "PRIMARY KEY (id), " + "UNIQUE (object_store_id, name), " + "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " + "CASCADE" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + // Table `index_data` + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE index_data (" + "id INTEGER, " + "index_id INTEGER NOT NULL, " + "object_data_id INTEGER NOT NULL, " + "object_data_key NOT NULL, " // NONE affinity + "value NOT NULL, " + "PRIMARY KEY (id), " + "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " + "CASCADE, " + "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " + "CASCADE" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX value_index " + "ON index_data (index_id, value);" + )); + NS_ENSURE_SUCCESS(rv, rv); + + // Table `unique_index_data` + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE unique_index_data (" + "id INTEGER, " + "index_id INTEGER NOT NULL, " + "object_data_id INTEGER NOT NULL, " + "object_data_key NOT NULL, " // NONE affinity + "value NOT NULL, " + "PRIMARY KEY (id), " + "UNIQUE (index_id, value), " + "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " + "CASCADE " + "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " + "CASCADE" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + // Table `ai_index_data` + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE ai_index_data (" + "id INTEGER, " + "index_id INTEGER NOT NULL, " + "ai_object_data_id INTEGER NOT NULL, " + "value NOT NULL, " + "PRIMARY KEY (id), " + "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " + "CASCADE, " + "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE " + "CASCADE" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX ai_value_index " + "ON ai_index_data (index_id, value);" + )); + NS_ENSURE_SUCCESS(rv, rv); + + // Table `ai_unique_index_data` + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE ai_unique_index_data (" + "id INTEGER, " + "index_id INTEGER NOT NULL, " + "ai_object_data_id INTEGER NOT NULL, " + "value NOT NULL, " + "PRIMARY KEY (id), " + "UNIQUE (index_id, value), " + "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " + "CASCADE, " + "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE " + "CASCADE" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aDBConn->SetSchemaVersion(DB_SCHEMA_VERSION); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +CreateMetaData(mozIStorageConnection* aConnection, + const nsAString& aName) +{ + NS_PRECONDITION(!NS_IsMainThread(), "Wrong thread!"); + NS_PRECONDITION(aConnection, "Null database!"); + + nsCOMPtr stmt; + nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "INSERT OR REPLACE INTO database (name, dataVersion) " + "VALUES (:name, :dataVersion)" + ), getter_AddRefs(stmt)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), aName); + NS_ENSURE_SUCCESS(rv, rv); + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("dataVersion"), + JS_STRUCTURED_CLONE_VERSION); + NS_ENSURE_SUCCESS(rv, rv); + + return stmt->Execute(); +} + +nsresult +UpgradeSchemaFrom4To5(mozIStorageConnection* aConnection) +{ + nsresult rv; + + mozStorageTransaction transaction(aConnection, false, + mozIStorageConnection::TRANSACTION_IMMEDIATE); + + // All we changed is the type of the version column, so lets try to + // convert that to an integer, and if we fail, set it to 0. + nsCOMPtr stmt; + rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "SELECT name, version, dataVersion " + "FROM database" + ), getter_AddRefs(stmt)); + NS_ENSURE_SUCCESS(rv, rv); + + nsString name; + PRInt32 intVersion; + PRInt64 dataVersion; + + { + mozStorageStatementScoper scoper(stmt); + + bool hasResults; + rv = stmt->ExecuteStep(&hasResults); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(hasResults, NS_ERROR_FAILURE); + + nsString version; + rv = stmt->GetString(1, version); + NS_ENSURE_SUCCESS(rv, rv); + + intVersion = version.ToInteger(&rv, 10); + if (NS_FAILED(rv)) { + intVersion = 0; + } + + rv = stmt->GetString(0, name); + NS_ENSURE_SUCCESS(rv, rv); + + rv = stmt->GetInt64(2, &dataVersion); + NS_ENSURE_SUCCESS(rv, rv); + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE database" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE database (" + "name TEXT NOT NULL, " + "version INTEGER NOT NULL DEFAULT 0, " + "dataVersion INTEGER NOT NULL" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "INSERT INTO database (name, version, dataVersion) " + "VALUES (:name, :version, :dataVersion)" + ), getter_AddRefs(stmt)); + NS_ENSURE_SUCCESS(rv, rv); + + { + mozStorageStatementScoper scoper(stmt); + + rv = stmt->BindStringParameter(0, name); + NS_ENSURE_SUCCESS(rv, rv); + + rv = stmt->BindInt32Parameter(1, intVersion); + NS_ENSURE_SUCCESS(rv, rv); + + rv = stmt->BindInt64Parameter(2, dataVersion); + NS_ENSURE_SUCCESS(rv, rv); + + rv = stmt->Execute(); + NS_ENSURE_SUCCESS(rv, rv); + } + + rv = aConnection->SetSchemaVersion(DB_SCHEMA_VERSION); + NS_ENSURE_SUCCESS(rv, rv); + + rv = transaction.Commit(); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +CreateDatabaseConnection(const nsAString& aName, + nsIFile* aDBFile, + mozIStorageConnection** aConnection) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + nsCOMPtr dbDirectory; + nsresult rv = aDBFile->GetParent(getter_AddRefs(dbDirectory)); + NS_ENSURE_SUCCESS(rv, rv); + + bool exists; + rv = aDBFile->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + + NS_NAMED_LITERAL_CSTRING(quotaVFSName, "quota"); + + nsCOMPtr ss = + do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID); + NS_ENSURE_TRUE(ss, NS_ERROR_FAILURE); + + nsCOMPtr connection; + rv = ss->OpenDatabaseWithVFS(aDBFile, quotaVFSName, + getter_AddRefs(connection)); + if (rv == NS_ERROR_FILE_CORRUPTED) { + // Nuke the database file. The web services can recreate their data. + rv = aDBFile->Remove(false); + NS_ENSURE_SUCCESS(rv, rv); + + exists = false; + + rv = ss->OpenDatabaseWithVFS(aDBFile, quotaVFSName, + getter_AddRefs(connection)); + } + NS_ENSURE_SUCCESS(rv, rv); + + // Check to make sure that the database schema is correct. + PRInt32 schemaVersion; + rv = connection->GetSchemaVersion(&schemaVersion); + NS_ENSURE_SUCCESS(rv, rv); + + if (schemaVersion != DB_SCHEMA_VERSION) { + // This logic needs to change next time we change the schema! + PR_STATIC_ASSERT(DB_SCHEMA_VERSION == 5); + if (schemaVersion == 4) { + rv = UpgradeSchemaFrom4To5(connection); + NS_ENSURE_SUCCESS(rv, rv); + } + else { + // Nuke it from orbit, it's the only way to be sure. + if (exists) { + // If the connection is not at the right schema version, nuke it. + rv = aDBFile->Remove(false); + NS_ENSURE_SUCCESS(rv, rv); + + rv = ss->OpenDatabaseWithVFS(aDBFile, quotaVFSName, + getter_AddRefs(connection)); + NS_ENSURE_SUCCESS(rv, rv); + } + + mozStorageTransaction transaction(connection, false, + mozIStorageConnection::TRANSACTION_IMMEDIATE); + rv = CreateTables(connection); + NS_ENSURE_SUCCESS(rv, rv); + + rv = CreateMetaData(connection, aName); + NS_ENSURE_SUCCESS(rv, rv); + + rv = transaction.Commit(); + NS_ENSURE_SUCCESS(rv, rv); + } + } + + // Check to make sure that the database schema is correct again. + NS_ASSERTION(NS_SUCCEEDED(connection->GetSchemaVersion(&schemaVersion)) && + schemaVersion == DB_SCHEMA_VERSION, + "CreateTables failed!"); + + // Turn on foreign key constraints. + rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "PRAGMA foreign_keys = ON;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + connection.forget(aConnection); + return NS_OK; +} + +class SetVersionHelper : public AsyncConnectionHelper, + public IDBTransactionListener +{ +public: + SetVersionHelper(IDBTransaction* aTransaction, + IDBOpenDBRequest* aRequest, + OpenDatabaseHelper* aHelper, + PRUint64 aRequestedVersion, + PRUint64 aCurrentVersion) + : AsyncConnectionHelper(aTransaction, aRequest), + mOpenRequest(aRequest), mOpenHelper(aHelper), + mRequestedVersion(aRequestedVersion), + mCurrentVersion(aCurrentVersion) + { + mTransaction->SetTransactionListener(this); + } + + NS_DECL_ISUPPORTS_INHERITED + + nsresult DoDatabaseWork(mozIStorageConnection* aConnection); + nsresult GetSuccessResult(JSContext* aCx, + jsval* aVal); + + // SetVersionHelper never fires an error event at the request. It hands that + // responsibility back to the OpenDatabaseHelper + void OnError() { } + + // Need an upgradeneeded event here. + already_AddRefed CreateSuccessEvent(); + + nsresult NotifyTransactionComplete(IDBTransaction* aTransaction); + +private: + // In-params + nsRefPtr mOpenHelper; + nsRefPtr mOpenRequest; + PRUint64 mRequestedVersion; + PRUint64 mCurrentVersion; +}; + +} // anonymous namespace + +NS_IMPL_THREADSAFE_ISUPPORTS1(OpenDatabaseHelper, nsIRunnable); + +nsresult +OpenDatabaseHelper::Dispatch(nsIEventTarget* aTarget) +{ + NS_ASSERTION(mState == eCreated, "We've already been dispatched?"); + mState = eDBWork; + + return aTarget->Dispatch(this, NS_DISPATCH_NORMAL); +} + +nsresult +OpenDatabaseHelper::RunImmediately() +{ + NS_ASSERTION(mState == eCreated, "We've already been dispatched?"); + NS_ASSERTION(NS_FAILED(mResultCode), + "Should only be short-circuiting if we failed!"); + NS_ASSERTION(NS_IsMainThread(), "All hell is about to break lose!"); + + mState = eFiringEvents; + return this->Run(); +} + +nsresult +OpenDatabaseHelper::DoDatabaseWork() +{ +#ifdef DEBUG + { + bool correctThread; + NS_ASSERTION(NS_SUCCEEDED(IndexedDatabaseManager::Get()->IOThread()-> + IsOnCurrentThread(&correctThread)) && + correctThread, + "Running on the wrong thread!"); + } +#endif + + if (IndexedDatabaseManager::IsShuttingDown()) { + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + nsCOMPtr dbFile; + nsresult rv = GetDatabaseFile(mASCIIOrigin, mName, getter_AddRefs(dbFile)); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = dbFile->GetPath(mDatabaseFilePath); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + nsCOMPtr dbDirectory; + rv = dbFile->GetParent(getter_AddRefs(dbDirectory)); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + bool exists; + rv = dbDirectory->Exists(&exists); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (exists) { + bool isDirectory; + rv = dbDirectory->IsDirectory(&isDirectory); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + NS_ENSURE_TRUE(isDirectory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + } + else { + rv = dbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + } + + IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get(); + NS_ASSERTION(mgr, "This should never be null!"); + + rv = mgr->EnsureQuotaManagementForDirectory(dbDirectory); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + nsCOMPtr connection; + rv = CreateDatabaseConnection(mName, dbFile, getter_AddRefs(connection)); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + // Get the data version. + nsCOMPtr stmt; + rv = connection->CreateStatement(NS_LITERAL_CSTRING( + "SELECT dataVersion " + "FROM database" + ), getter_AddRefs(stmt)); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + bool hasResult; + rv = stmt->ExecuteStep(&hasResult); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (!hasResult) { + NS_ERROR("Database has no dataVersion!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + PRInt64 dataVersion; + rv = stmt->GetInt64(0, &dataVersion); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (dataVersion > JS_STRUCTURED_CLONE_VERSION) { + NS_ERROR("Bad data version!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + if (dataVersion < JS_STRUCTURED_CLONE_VERSION) { + // Need to upgrade the database, here, before returning to the main thread. + NS_NOTYETIMPLEMENTED("Implement me!"); + } + + mDatabaseId = HashString(mDatabaseFilePath); + NS_ASSERTION(mDatabaseId, "HashString gave us 0?!"); + + rv = IDBFactory::LoadDatabaseInformation(connection, mDatabaseId, &mCurrentVersion, + mObjectStores); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + for (PRUint32 i = 0; i < mObjectStores.Length(); i++) { + nsAutoPtr& objectStoreInfo = mObjectStores[i]; + for (PRUint32 j = 0; j < objectStoreInfo->indexes.Length(); j++) { + IndexInfo& indexInfo = objectStoreInfo->indexes[j]; + mLastIndexId = NS_MAX(indexInfo.id, mLastIndexId); + } + mLastObjectStoreId = NS_MAX(objectStoreInfo->id, mLastObjectStoreId); + } + + // See if we need to do a VERSION_CHANGE transaction + if (mCurrentVersion > mRequestedVersion) { + return NS_ERROR_DOM_INDEXEDDB_VERSION_ERR; + } + + mState = mCurrentVersion != mRequestedVersion ? + eSetVersionPending : + eFiringEvents; + return NS_OK; +} + +nsresult +OpenDatabaseHelper::StartSetVersion() +{ + NS_ASSERTION(mState == eSetVersionPending, "Why are we here?"); + + // In case we fail, fire error events + mState = eFiringEvents; + + nsresult rv = EnsureSuccessResult(); + NS_ENSURE_SUCCESS(rv, rv); + + nsTArray storesToOpen; + nsRefPtr transaction = + IDBTransaction::Create(mDatabase, storesToOpen, + IDBTransaction::VERSION_CHANGE, + kDefaultDatabaseTimeoutSeconds, true); + NS_ENSURE_TRUE(transaction, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + nsRefPtr helper = + new SetVersionHelper(transaction, mOpenDBRequest, this, mRequestedVersion, + mCurrentVersion); + + IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get(); + NS_ASSERTION(mgr, "This should never be null!"); + + rv = mgr->SetDatabaseVersion(mDatabase, mOpenDBRequest, mCurrentVersion, + mRequestedVersion, helper); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + // The SetVersionHelper is responsible for dispatching us back to the + // main thread again and changing the state to eSetVersionCompleted. + mState = eSetVersionPending; + return NS_OK; +} + +NS_IMETHODIMP +OpenDatabaseHelper::Run() +{ + NS_ASSERTION(mState != eCreated, "Dispatch was not called?!?"); + + if (NS_IsMainThread()) { + // If we need to queue up a SetVersionHelper, do that here. + if (mState == eSetVersionPending) { + nsresult rv = StartSetVersion(); + + if (NS_SUCCEEDED(rv)) { + return rv; + } + + SetError(rv); + // fall through and run the default error processing + } + + // We've done whatever work we need to do on the DB thread, and any + // SetVersion stuff is done by now. + NS_ASSERTION(mState == eFiringEvents || + mState == eSetVersionCompleted, "Why are we here?"); + + if (mState == eSetVersionCompleted) { + mState = eFiringEvents; + } else { + // Notify the request that we're done, but only if we didn't just finish + // a SetVersionHelper. In the SetVersionHelper case, that helper tells + // the request that it is done, and we avoid calling NotifyHandlerCompleted + // twice. + + nsresult rv = mOpenDBRequest->NotifyHelperCompleted(this); + if (NS_SUCCEEDED(mResultCode) && NS_FAILED(rv)) { + mResultCode = rv; + } + } + + NS_ASSERTION(mState == eFiringEvents, "Why are we here?"); + + if (NS_FAILED(mResultCode)) { + DispatchErrorEvent(); + } else { + DispatchSuccessEvent(); + } + + return NS_OK; + } + + // If we're on the DB thread, do that + NS_ASSERTION(mState == eDBWork, "Why are we here?"); + mResultCode = DoDatabaseWork(); + NS_ASSERTION(mState != eDBWork, "We should be doing something else now."); + + return NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL); +} + +nsresult +OpenDatabaseHelper::EnsureSuccessResult() +{ + DatabaseInfo* dbInfo; + if (DatabaseInfo::Get(mDatabaseId, &dbInfo)) { + NS_ASSERTION(dbInfo->referenceCount, "Bad reference count!"); + ++dbInfo->referenceCount; + +#ifdef DEBUG + { + NS_ASSERTION(dbInfo->name == mName && + dbInfo->version == mCurrentVersion && + dbInfo->id == mDatabaseId && + dbInfo->filePath == mDatabaseFilePath, + "Metadata mismatch!"); + + PRUint32 objectStoreCount = mObjectStores.Length(); + for (PRUint32 index = 0; index < objectStoreCount; index++) { + nsAutoPtr& info = mObjectStores[index]; + NS_ASSERTION(info->databaseId == mDatabaseId, "Huh?!"); + + ObjectStoreInfo* otherInfo; + NS_ASSERTION(ObjectStoreInfo::Get(mDatabaseId, info->name, &otherInfo), + "ObjectStore not known!"); + + NS_ASSERTION(info->name == otherInfo->name && + info->id == otherInfo->id && + info->keyPath == otherInfo->keyPath && + info->autoIncrement == otherInfo->autoIncrement && + info->databaseId == otherInfo->databaseId, + "Metadata mismatch!"); + NS_ASSERTION(dbInfo->ContainsStoreName(info->name), + "Object store names out of date!"); + NS_ASSERTION(info->indexes.Length() == otherInfo->indexes.Length(), + "Bad index length!"); + + PRUint32 indexCount = info->indexes.Length(); + for (PRUint32 indexIndex = 0; indexIndex < indexCount; indexIndex++) { + const IndexInfo& indexInfo = info->indexes[indexIndex]; + const IndexInfo& otherIndexInfo = otherInfo->indexes[indexIndex]; + NS_ASSERTION(indexInfo.id == otherIndexInfo.id, + "Bad index id!"); + NS_ASSERTION(indexInfo.name == otherIndexInfo.name, + "Bad index name!"); + NS_ASSERTION(indexInfo.keyPath == otherIndexInfo.keyPath, + "Bad index keyPath!"); + NS_ASSERTION(indexInfo.unique == otherIndexInfo.unique, + "Bad index unique value!"); + NS_ASSERTION(indexInfo.autoIncrement == otherIndexInfo.autoIncrement, + "Bad index autoIncrement value!"); + } + } + } +#endif + + } + else { + nsAutoPtr newInfo(new DatabaseInfo()); + + newInfo->name = mName; + newInfo->id = mDatabaseId; + newInfo->filePath = mDatabaseFilePath; + newInfo->referenceCount = 1; + + if (!DatabaseInfo::Put(newInfo)) { + NS_ERROR("Failed to add to hash!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + dbInfo = newInfo.forget(); + + nsresult rv = IDBFactory::UpdateDatabaseMetadata(dbInfo, mCurrentVersion, + mObjectStores); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + NS_ASSERTION(mObjectStores.IsEmpty(), "Should have swapped!"); + } + + dbInfo->nextObjectStoreId = mLastObjectStoreId + 1; + dbInfo->nextIndexId = mLastIndexId + 1; + + nsRefPtr database = + IDBDatabase::Create(mOpenDBRequest->ScriptContext(), + mOpenDBRequest->Owner(), dbInfo, mASCIIOrigin); + if (!database) { + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + NS_ASSERTION(!mDatabase, "Shouldn't have a database yet!"); + mDatabase.swap(database); + + return NS_OK; +} + +nsresult +OpenDatabaseHelper::GetSuccessResult(JSContext* aCx, + jsval* aVal) +{ + // Be careful not to load the database twice. + if (!mDatabase) { + nsresult rv = EnsureSuccessResult(); + NS_ENSURE_SUCCESS(rv, rv); + } + + return WrapNative(aCx, NS_ISUPPORTS_CAST(nsIDOMEventTarget*, mDatabase), + aVal); +} + +nsresult +OpenDatabaseHelper::NotifySetVersionFinished() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread"); + NS_ASSERTION(mState = eSetVersionPending, "How did we get here?"); + + mState = eSetVersionCompleted; + + // Dispatch ourself back to the main thread + return NS_DispatchToCurrentThread(this); +} + +void +OpenDatabaseHelper::DispatchSuccessEvent() +{ + NS_ASSERTION(mDatabase, "Doesn't seem very successful to me."); + + nsRefPtr event = + CreateGenericEvent(NS_LITERAL_STRING(SUCCESS_EVT_STR)); + if (!event) { + NS_ERROR("Failed to create event!"); + return; + } + + bool dummy; + mOpenDBRequest->DispatchEvent(event, &dummy); +} + +void +OpenDatabaseHelper::DispatchErrorEvent() +{ + nsRefPtr event = + CreateGenericEvent(NS_LITERAL_STRING(ERROR_EVT_STR)); + if (!event) { + NS_ERROR("Failed to create event!"); + return; + } + + PRUint16 errorCode = 0; + DebugOnly rv = + mOpenDBRequest->GetErrorCode(&errorCode); + NS_ASSERTION(NS_SUCCEEDED(rv), "This shouldn't be failing at this point!"); + if (!errorCode) { + mOpenDBRequest->SetError(mResultCode); + } + + bool dummy; + mOpenDBRequest->DispatchEvent(event, &dummy); +} + +NS_IMPL_ISUPPORTS_INHERITED0(SetVersionHelper, AsyncConnectionHelper); + +nsresult +SetVersionHelper::DoDatabaseWork(mozIStorageConnection* aConnection) +{ + NS_ASSERTION(aConnection, "Passing a null connection!"); + + nsCOMPtr stmt; + nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "UPDATE database " + "SET version = :version" + ), getter_AddRefs(stmt)); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("version"), + mRequestedVersion); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (NS_FAILED(stmt->Execute())) { + return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR; + } + + return NS_OK; +} + +nsresult +SetVersionHelper::GetSuccessResult(JSContext* aCx, + jsval* aVal) +{ + DatabaseInfo* info; + if (!DatabaseInfo::Get(mDatabase->Id(), &info)) { + NS_ERROR("This should never fail!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + info->version = mRequestedVersion; + + NS_ASSERTION(mTransaction, "Better have a transaction!"); + + mOpenRequest->SetTransaction(mTransaction); + + return WrapNative(aCx, NS_ISUPPORTS_CAST(nsIDOMEventTarget*, mDatabase), + aVal); +} + +already_AddRefed +SetVersionHelper::CreateSuccessEvent() +{ + NS_ASSERTION(mCurrentVersion < mRequestedVersion, "Huh?"); + + return IDBVersionChangeEvent::CreateUpgradeNeeded(mCurrentVersion, + mRequestedVersion); +} + +nsresult +SetVersionHelper::NotifyTransactionComplete(IDBTransaction* aTransaction) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aTransaction, "This is unexpected."); + NS_ASSERTION(mOpenRequest, "Why don't we have a request?"); + + // If we hit an error, the OpenDatabaseHelper needs to get that error too. + nsresult rv = GetResultCode(); + if (NS_FAILED(rv)) { + mOpenHelper->SetError(rv); + } + + // If the transaction was aborted, we should throw an error message. + if (aTransaction->IsAborted()) { + mOpenHelper->SetError(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR); + } + + mOpenRequest->SetTransaction(nsnull); + + rv = mOpenHelper->NotifySetVersionFinished(); + mOpenHelper = nsnull; + + return rv; +} diff --git a/dom/indexedDB/OpenDatabaseHelper.h b/dom/indexedDB/OpenDatabaseHelper.h new file mode 100644 index 000000000000..cf9e5c64109a --- /dev/null +++ b/dom/indexedDB/OpenDatabaseHelper.h @@ -0,0 +1,129 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Indexed Database. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Ben Turner + * Kyle Huey + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef mozilla_dom_indexeddb_opendatabasehelper_h__ +#define mozilla_dom_indexeddb_opendatabasehelper_h__ + +#include "AsyncConnectionHelper.h" +#include "DatabaseInfo.h" +#include "IDBDatabase.h" +#include "IDBRequest.h" + +#include "nsIRunnable.h" + +class mozIStorageConnection; + +BEGIN_INDEXEDDB_NAMESPACE + +class OpenDatabaseHelper : public HelperBase +{ +public: + OpenDatabaseHelper(IDBOpenDBRequest* aRequest, + const nsAString& aName, + const nsACString& aASCIIOrigin, + PRUint64 aRequestedVersion) + : HelperBase(aRequest), mOpenDBRequest(aRequest), mName(aName), + mASCIIOrigin(aASCIIOrigin), mRequestedVersion(aRequestedVersion), + mCurrentVersion(0), mDataVersion(DB_SCHEMA_VERSION), mDatabaseId(0), + mLastObjectStoreId(0), mLastIndexId(0), mState(eCreated), + mResultCode(NS_OK) + { } + + NS_DECL_ISUPPORTS + NS_DECL_NSIRUNNABLE + + nsresult Dispatch(nsIEventTarget* aDatabaseThread); + nsresult RunImmediately(); + + void SetError(nsresult rv) + { + NS_ASSERTION(NS_FAILED(rv), "Why are you telling me?"); + mResultCode = rv; + } + + nsresult GetResultCode() + { + return mResultCode; + } + + nsresult NotifySetVersionFinished(); + +protected: + // Methods only called on the main thread + nsresult EnsureSuccessResult(); + nsresult StartSetVersion(); + nsresult GetSuccessResult(JSContext* aCx, + jsval* aVal); + void DispatchSuccessEvent(); + void DispatchErrorEvent(); + + // Methods only called on the DB thread + nsresult DoDatabaseWork(); + +private: + // In-params. + nsRefPtr mOpenDBRequest; + nsString mName; + nsCString mASCIIOrigin; + PRUint64 mRequestedVersion; + + // Out-params. + nsTArray > mObjectStores; + PRUint64 mCurrentVersion; + PRUint32 mDataVersion; + nsString mDatabaseFilePath; + PRUint32 mDatabaseId; + PRInt64 mLastObjectStoreId; + PRInt64 mLastIndexId; + nsRefPtr mDatabase; + + // State variables + enum OpenDatabaseState { + eCreated = 0, // Not yet dispatched to the DB thread + eDBWork, // Waiting to do/doing work on the DB thread + eFiringEvents, // Waiting to fire/firing events on the main thread + eSetVersionPending, // Waiting on a SetVersionHelper + eSetVersionCompleted, // SetVersionHelper is done + }; + OpenDatabaseState mState; + nsresult mResultCode; +}; + +END_INDEXEDDB_NAMESPACE + +#endif // mozilla_dom_indexeddb_opendatabasehelper_h__ diff --git a/dom/indexedDB/nsIIDBDatabase.idl b/dom/indexedDB/nsIIDBDatabase.idl index 5f550fb13ee4..83d23700196d 100644 --- a/dom/indexedDB/nsIIDBDatabase.idl +++ b/dom/indexedDB/nsIIDBDatabase.idl @@ -51,12 +51,12 @@ interface nsIDOMEventListener; * http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBDatabase * for more information. */ -[scriptable, uuid(42b38d02-1a29-45f0-99ef-04fd5b441270)] +[scriptable, uuid(ac14faa5-261c-4a84-9616-a700fd606f83)] interface nsIIDBDatabase : nsISupports { readonly attribute DOMString name; - readonly attribute DOMString version; + readonly attribute unsigned long long version; readonly attribute nsIDOMDOMStringList objectStoreNames; @@ -76,10 +76,6 @@ interface nsIIDBDatabase : nsISupports void deleteObjectStore(in AString name); - [implicit_jscontext] - nsIIDBRequest - setVersion(in AString version); - [optional_argc, implicit_jscontext] nsIIDBTransaction transaction(in nsIVariant storeNames, // js array of strings diff --git a/dom/indexedDB/nsIIDBFactory.idl b/dom/indexedDB/nsIIDBFactory.idl index fcd3c06be487..8ed0fe18af16 100644 --- a/dom/indexedDB/nsIIDBFactory.idl +++ b/dom/indexedDB/nsIIDBFactory.idl @@ -41,7 +41,7 @@ #include "nsISupports.idl" interface nsIIDBKeyRange; -interface nsIIDBRequest; +interface nsIIDBOpenDBRequest; interface nsIVariant; /** @@ -49,10 +49,10 @@ interface nsIVariant; * http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBFactory * for more information. */ -[scriptable, uuid(137d17a5-fac5-4788-87b5-bc3806d7cfaf)] +[scriptable, uuid(4b23254a-ce6d-4442-8c90-9d8744d3c633)] interface nsIIDBFactory : nsISupports { [implicit_jscontext] - nsIIDBRequest - open(in AString name); + nsIIDBOpenDBRequest + open(in AString name, in long long version); }; diff --git a/dom/indexedDB/nsIIDBVersionChangeRequest.idl b/dom/indexedDB/nsIIDBOpenDBRequest.idl similarity index 87% rename from dom/indexedDB/nsIIDBVersionChangeRequest.idl rename to dom/indexedDB/nsIIDBOpenDBRequest.idl index e35508d6c0e7..19b26344b8de 100644 --- a/dom/indexedDB/nsIIDBVersionChangeRequest.idl +++ b/dom/indexedDB/nsIIDBOpenDBRequest.idl @@ -43,12 +43,13 @@ interface nsIDOMEventListener; /** - * IDBReqeust interface. See - * http://dev.w3.org/2006/webapi/WebSimpleDB/#idl-def-IDBRequest for more - * information. + * IDBOpenDBRequest interface. See + * http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBOpenDBRequest + * for more information. */ -[scriptable, uuid(aeaabb0d-594a-4c58-ac5e-68ef3bff927d)] -interface nsIIDBVersionChangeRequest : nsISupports +[scriptable, uuid(91010fbe-1dfb-435d-852e-288d2804c0a7)] +interface nsIIDBOpenDBRequest : nsISupports { attribute nsIDOMEventListener onblocked; + attribute nsIDOMEventListener onupgradeneeded; }; diff --git a/dom/indexedDB/nsIIDBVersionChangeEvent.idl b/dom/indexedDB/nsIIDBVersionChangeEvent.idl index 41d45068d1ac..0710ac385b37 100644 --- a/dom/indexedDB/nsIIDBVersionChangeEvent.idl +++ b/dom/indexedDB/nsIIDBVersionChangeEvent.idl @@ -39,8 +39,9 @@ #include "nsIDOMEvent.idl" -[scriptable, uuid(6a232c30-1bc4-4d5b-9ce0-6e7c08934755)] +[scriptable, uuid(2c5159dc-7d71-4fc6-a3b3-884ed7586456)] interface nsIIDBVersionChangeEvent : nsIDOMEvent { - readonly attribute AString version; + readonly attribute unsigned long long oldVersion; + readonly attribute unsigned long long newVersion; }; diff --git a/dom/indexedDB/test/bfcache_iframe1.html b/dom/indexedDB/test/bfcache_iframe1.html index e8c3a3ee80e2..dc4e37dc0eb4 100644 --- a/dom/indexedDB/test/bfcache_iframe1.html +++ b/dom/indexedDB/test/bfcache_iframe1.html @@ -2,14 +2,14 @@ diff --git a/dom/indexedDB/test/bfcache_iframe2.html b/dom/indexedDB/test/bfcache_iframe2.html index 1b7f35767b48..0f596fdf5d2b 100644 --- a/dom/indexedDB/test/bfcache_iframe2.html +++ b/dom/indexedDB/test/bfcache_iframe2.html @@ -2,26 +2,24 @@ diff --git a/dom/indexedDB/test/browser_forgetThisSite.js b/dom/indexedDB/test/browser_forgetThisSite.js index 7085da699eba..8478369d0ac7 100644 --- a/dom/indexedDB/test/browser_forgetThisSite.js +++ b/dom/indexedDB/test/browser_forgetThisSite.js @@ -33,7 +33,7 @@ function test1() gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true); setFinishedCallback(function(result, exception) { - ok(result == "11", "Set version on database in " + testPageURL1); + ok(result == 11, "Set version on database in " + testPageURL1); ok(!exception, "No exception"); gBrowser.removeCurrentTab(); @@ -51,7 +51,7 @@ function test2() gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true); setFinishedCallback(function(result, exception) { - ok(result == "11", "Set version on database in " + testPageURL2); + ok(result == 11, "Set version on database in " + testPageURL2); ok(!exception, "No exception"); gBrowser.removeCurrentTab(); @@ -79,7 +79,7 @@ function test4() gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true); setFinishedCallback(function(result, exception) { - ok(result == "11", "Got correct version on database in " + testPageURL3); + ok(result == 11, "Got correct version on database in " + testPageURL3); ok(!exception, "No exception"); gBrowser.removeCurrentTab(); @@ -97,7 +97,9 @@ function test5() gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true); setFinishedCallback(function(result, exception) { - ok(result == "", "Got correct version on database in " + testPageURL4); + // XXXkhuey this isn't really testing anything until we get the default + // version behavior implemented ... + ok(result == 11, "Got correct version on database in " + testPageURL4); ok(!exception, "No exception"); gBrowser.removeCurrentTab(); diff --git a/dom/indexedDB/test/browser_forgetThisSiteAdd.html b/dom/indexedDB/test/browser_forgetThisSiteAdd.html index 5b698aadf727..2a7525d4ec64 100644 --- a/dom/indexedDB/test/browser_forgetThisSiteAdd.html +++ b/dom/indexedDB/test/browser_forgetThisSiteAdd.html @@ -9,9 +9,9 @@