michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef mozilla_dom_indexeddb_idbtransaction_h__ michael@0: #define mozilla_dom_indexeddb_idbtransaction_h__ michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/dom/indexedDB/IndexedDatabase.h" michael@0: michael@0: #include "mozIStorageConnection.h" michael@0: #include "mozIStorageStatement.h" michael@0: #include "mozIStorageFunction.h" michael@0: #include "mozilla/dom/DOMError.h" michael@0: #include "nsIRunnable.h" michael@0: michael@0: #include "nsAutoPtr.h" michael@0: #include "nsClassHashtable.h" michael@0: #include "nsHashKeys.h" michael@0: #include "nsInterfaceHashtable.h" michael@0: #include "nsRefPtrHashtable.h" michael@0: michael@0: #include "mozilla/dom/IDBTransactionBinding.h" michael@0: #include "mozilla/dom/indexedDB/IDBDatabase.h" michael@0: #include "mozilla/dom/indexedDB/IDBWrapperCache.h" michael@0: #include "mozilla/dom/indexedDB/FileInfo.h" michael@0: michael@0: class nsIThread; michael@0: class nsPIDOMWindow; michael@0: michael@0: namespace mozilla { michael@0: class EventChainPreVisitor; michael@0: } // namespace mozilla michael@0: michael@0: BEGIN_INDEXEDDB_NAMESPACE michael@0: michael@0: class AsyncConnectionHelper; michael@0: class CommitHelper; michael@0: class IDBRequest; michael@0: class IndexedDBDatabaseChild; michael@0: class IndexedDBTransactionChild; michael@0: class IndexedDBTransactionParent; michael@0: struct ObjectStoreInfo; michael@0: class TransactionThreadPool; michael@0: class UpdateRefcountFunction; michael@0: michael@0: class IDBTransactionListener michael@0: { michael@0: public: michael@0: NS_IMETHOD_(MozExternalRefCountType) AddRef() = 0; michael@0: NS_IMETHOD_(MozExternalRefCountType) Release() = 0; michael@0: michael@0: // Called just before dispatching the final events on the transaction. michael@0: virtual nsresult NotifyTransactionPreComplete(IDBTransaction* aTransaction) = 0; michael@0: // Called just after dispatching the final events on the transaction. michael@0: virtual nsresult NotifyTransactionPostComplete(IDBTransaction* aTransaction) = 0; michael@0: }; michael@0: michael@0: class IDBTransaction : public IDBWrapperCache, michael@0: public nsIRunnable michael@0: { michael@0: friend class AsyncConnectionHelper; michael@0: friend class CommitHelper; michael@0: friend class IndexedDBDatabaseChild; michael@0: friend class ThreadObserver; michael@0: friend class TransactionThreadPool; michael@0: michael@0: public: michael@0: NS_DECL_ISUPPORTS_INHERITED michael@0: NS_DECL_NSIRUNNABLE michael@0: michael@0: NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBTransaction, IDBWrapperCache) michael@0: michael@0: enum Mode michael@0: { michael@0: READ_ONLY = 0, michael@0: READ_WRITE, michael@0: VERSION_CHANGE, michael@0: michael@0: // Only needed for IPC serialization helper, should never be used in code. michael@0: MODE_INVALID michael@0: }; michael@0: michael@0: enum ReadyState michael@0: { michael@0: INITIAL = 0, michael@0: LOADING, michael@0: COMMITTING, michael@0: DONE michael@0: }; michael@0: michael@0: static already_AddRefed michael@0: Create(IDBDatabase* aDatabase, michael@0: const Sequence& aObjectStoreNames, michael@0: Mode aMode, michael@0: bool aDispatchDelayed) michael@0: { michael@0: return CreateInternal(aDatabase, aObjectStoreNames, aMode, aDispatchDelayed, michael@0: false); michael@0: } michael@0: michael@0: // nsIDOMEventTarget michael@0: virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) MOZ_OVERRIDE; michael@0: michael@0: void OnNewRequest(); michael@0: void OnRequestFinished(); michael@0: void OnRequestDisconnected(); michael@0: michael@0: void RemoveObjectStore(const nsAString& aName); michael@0: michael@0: void SetTransactionListener(IDBTransactionListener* aListener); michael@0: michael@0: bool StartSavepoint(); michael@0: nsresult ReleaseSavepoint(); michael@0: void RollbackSavepoint(); michael@0: michael@0: // Only meant to be called on mStorageThread! michael@0: nsresult GetOrCreateConnection(mozIStorageConnection** aConnection); michael@0: michael@0: already_AddRefed michael@0: GetCachedStatement(const nsACString& aQuery); michael@0: michael@0: template michael@0: already_AddRefed michael@0: GetCachedStatement(const char (&aQuery)[N]) michael@0: { michael@0: return GetCachedStatement(NS_LITERAL_CSTRING(aQuery)); michael@0: } michael@0: michael@0: bool IsOpen() const; michael@0: michael@0: bool IsFinished() const michael@0: { michael@0: return mReadyState > LOADING; michael@0: } michael@0: michael@0: bool IsWriteAllowed() const michael@0: { michael@0: return mMode == READ_WRITE || mMode == VERSION_CHANGE; michael@0: } michael@0: michael@0: bool IsAborted() const michael@0: { michael@0: return NS_FAILED(mAbortCode); michael@0: } michael@0: michael@0: // 'Get' prefix is to avoid name collisions with the enum michael@0: Mode GetMode() michael@0: { michael@0: return mMode; michael@0: } michael@0: michael@0: IDBDatabase* Database() michael@0: { michael@0: NS_ASSERTION(mDatabase, "This should never be null!"); michael@0: return mDatabase; michael@0: } michael@0: michael@0: DatabaseInfo* DBInfo() const michael@0: { michael@0: return mDatabaseInfo; michael@0: } michael@0: michael@0: already_AddRefed michael@0: GetOrCreateObjectStore(const nsAString& aName, michael@0: ObjectStoreInfo* aObjectStoreInfo, michael@0: bool aCreating); michael@0: michael@0: already_AddRefed GetFileInfo(nsIDOMBlob* aBlob); michael@0: void AddFileInfo(nsIDOMBlob* aBlob, FileInfo* aFileInfo); michael@0: michael@0: void ClearCreatedFileInfos(); michael@0: michael@0: void michael@0: SetActor(IndexedDBTransactionChild* aActorChild) michael@0: { michael@0: NS_ASSERTION(!aActorChild || !mActorChild, "Shouldn't have more than one!"); michael@0: mActorChild = aActorChild; michael@0: } michael@0: michael@0: void michael@0: SetActor(IndexedDBTransactionParent* aActorParent) michael@0: { michael@0: NS_ASSERTION(!aActorParent || !mActorParent, michael@0: "Shouldn't have more than one!"); michael@0: mActorParent = aActorParent; michael@0: } michael@0: michael@0: IndexedDBTransactionChild* michael@0: GetActorChild() const michael@0: { michael@0: return mActorChild; michael@0: } michael@0: michael@0: IndexedDBTransactionParent* michael@0: GetActorParent() const michael@0: { michael@0: return mActorParent; michael@0: } michael@0: michael@0: nsresult michael@0: Abort(IDBRequest* aRequest); michael@0: michael@0: nsresult michael@0: Abort(nsresult aAbortCode); michael@0: michael@0: nsresult michael@0: GetAbortCode() const michael@0: { michael@0: return mAbortCode; michael@0: } michael@0: michael@0: #ifdef MOZ_ENABLE_PROFILER_SPS michael@0: uint64_t michael@0: GetSerialNumber() const michael@0: { michael@0: return mSerialNumber; michael@0: } michael@0: #endif michael@0: michael@0: // nsWrapperCache michael@0: virtual JSObject* michael@0: WrapObject(JSContext* aCx) MOZ_OVERRIDE; michael@0: michael@0: // WebIDL michael@0: nsPIDOMWindow* michael@0: GetParentObject() const michael@0: { michael@0: return GetOwner(); michael@0: } michael@0: michael@0: IDBTransactionMode michael@0: GetMode(ErrorResult& aRv) const; michael@0: michael@0: IDBDatabase* michael@0: Db() const michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: return mDatabase; michael@0: } michael@0: michael@0: DOMError* michael@0: GetError(ErrorResult& aRv); michael@0: michael@0: already_AddRefed michael@0: ObjectStore(const nsAString& aName, ErrorResult& aRv); michael@0: michael@0: void michael@0: Abort(ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: aRv = AbortInternal(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR, nullptr); michael@0: } michael@0: michael@0: IMPL_EVENT_HANDLER(abort) michael@0: IMPL_EVENT_HANDLER(complete) michael@0: IMPL_EVENT_HANDLER(error) michael@0: michael@0: already_AddRefed michael@0: GetObjectStoreNames(ErrorResult& aRv); michael@0: michael@0: private: michael@0: nsresult michael@0: AbortInternal(nsresult aAbortCode, michael@0: already_AddRefed aError); michael@0: michael@0: // Should only be called directly through IndexedDBDatabaseChild. michael@0: static already_AddRefed michael@0: CreateInternal(IDBDatabase* aDatabase, michael@0: const Sequence& aObjectStoreNames, michael@0: Mode aMode, michael@0: bool aDispatchDelayed, michael@0: bool aIsVersionChangeTransactionChild); michael@0: michael@0: IDBTransaction(IDBDatabase* aDatabase); michael@0: ~IDBTransaction(); michael@0: michael@0: nsresult CommitOrRollback(); michael@0: michael@0: nsRefPtr mDatabase; michael@0: nsRefPtr mDatabaseInfo; michael@0: nsRefPtr mError; michael@0: nsTArray mObjectStoreNames; michael@0: ReadyState mReadyState; michael@0: Mode mMode; michael@0: uint32_t mPendingRequests; michael@0: michael@0: nsInterfaceHashtable michael@0: mCachedStatements; michael@0: michael@0: nsRefPtr mListener; michael@0: michael@0: // Only touched on the database thread. michael@0: nsCOMPtr mConnection; michael@0: michael@0: // Only touched on the database thread. michael@0: uint32_t mSavepointCount; michael@0: michael@0: nsTArray > mCreatedObjectStores; michael@0: nsTArray > mDeletedObjectStores; michael@0: michael@0: nsRefPtr mUpdateFileRefcountFunction; michael@0: nsRefPtrHashtable mCreatedFileInfos; michael@0: michael@0: IndexedDBTransactionChild* mActorChild; michael@0: IndexedDBTransactionParent* mActorParent; michael@0: michael@0: nsresult mAbortCode; michael@0: #ifdef MOZ_ENABLE_PROFILER_SPS michael@0: uint64_t mSerialNumber; michael@0: #endif michael@0: bool mCreating; michael@0: michael@0: #ifdef DEBUG michael@0: bool mFiredCompleteOrAbort; michael@0: #endif michael@0: }; michael@0: michael@0: class CommitHelper MOZ_FINAL : public nsIRunnable michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSIRUNNABLE michael@0: michael@0: CommitHelper(IDBTransaction* aTransaction, michael@0: IDBTransactionListener* aListener, michael@0: const nsTArray >& mUpdatedObjectStores); michael@0: CommitHelper(IDBTransaction* aTransaction, michael@0: nsresult aAbortCode); michael@0: ~CommitHelper(); michael@0: michael@0: template michael@0: bool AddDoomedObject(nsCOMPtr& aCOMPtr) michael@0: { michael@0: if (aCOMPtr) { michael@0: if (!mDoomedObjects.AppendElement(do_QueryInterface(aCOMPtr))) { michael@0: NS_ERROR("Out of memory!"); michael@0: return false; michael@0: } michael@0: aCOMPtr = nullptr; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: private: michael@0: // Writes new autoincrement counts to database michael@0: nsresult WriteAutoIncrementCounts(); michael@0: michael@0: // Updates counts after a successful commit michael@0: void CommitAutoIncrementCounts(); michael@0: michael@0: // Reverts counts when a transaction is aborted michael@0: void RevertAutoIncrementCounts(); michael@0: michael@0: nsRefPtr mTransaction; michael@0: nsRefPtr mListener; michael@0: nsCOMPtr mConnection; michael@0: nsRefPtr mUpdateFileRefcountFunction; michael@0: nsAutoTArray, 10> mDoomedObjects; michael@0: nsAutoTArray, 10> mAutoIncrementObjectStores; michael@0: michael@0: nsresult mAbortCode; michael@0: }; michael@0: michael@0: class UpdateRefcountFunction MOZ_FINAL : public mozIStorageFunction michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_MOZISTORAGEFUNCTION michael@0: michael@0: UpdateRefcountFunction(FileManager* aFileManager) michael@0: : mFileManager(aFileManager), mInSavepoint(false) michael@0: { } michael@0: michael@0: ~UpdateRefcountFunction() michael@0: { } michael@0: michael@0: void StartSavepoint() michael@0: { michael@0: MOZ_ASSERT(!mInSavepoint); michael@0: MOZ_ASSERT(!mSavepointEntriesIndex.Count()); michael@0: michael@0: mInSavepoint = true; michael@0: } michael@0: michael@0: void ReleaseSavepoint() michael@0: { michael@0: MOZ_ASSERT(mInSavepoint); michael@0: michael@0: mSavepointEntriesIndex.Clear(); michael@0: michael@0: mInSavepoint = false; michael@0: } michael@0: michael@0: void RollbackSavepoint() michael@0: { michael@0: MOZ_ASSERT(mInSavepoint); michael@0: michael@0: mInSavepoint = false; michael@0: michael@0: mSavepointEntriesIndex.EnumerateRead(RollbackSavepointCallback, nullptr); michael@0: michael@0: mSavepointEntriesIndex.Clear(); michael@0: } michael@0: michael@0: void ClearFileInfoEntries() michael@0: { michael@0: mFileInfoEntries.Clear(); michael@0: } michael@0: michael@0: nsresult WillCommit(mozIStorageConnection* aConnection); michael@0: void DidCommit(); michael@0: void DidAbort(); michael@0: michael@0: private: michael@0: class FileInfoEntry michael@0: { michael@0: public: michael@0: FileInfoEntry(FileInfo* aFileInfo) michael@0: : mFileInfo(aFileInfo), mDelta(0), mSavepointDelta(0) michael@0: { } michael@0: michael@0: ~FileInfoEntry() michael@0: { } michael@0: michael@0: nsRefPtr mFileInfo; michael@0: int32_t mDelta; michael@0: int32_t mSavepointDelta; michael@0: }; michael@0: michael@0: enum UpdateType { michael@0: eIncrement, michael@0: eDecrement michael@0: }; michael@0: michael@0: class DatabaseUpdateFunction michael@0: { michael@0: public: michael@0: DatabaseUpdateFunction(mozIStorageConnection* aConnection, michael@0: UpdateRefcountFunction* aFunction) michael@0: : mConnection(aConnection), mFunction(aFunction), mErrorCode(NS_OK) michael@0: { } michael@0: michael@0: bool Update(int64_t aId, int32_t aDelta); michael@0: nsresult ErrorCode() michael@0: { michael@0: return mErrorCode; michael@0: } michael@0: michael@0: private: michael@0: nsresult UpdateInternal(int64_t aId, int32_t aDelta); michael@0: michael@0: nsCOMPtr mConnection; michael@0: nsCOMPtr mUpdateStatement; michael@0: nsCOMPtr mSelectStatement; michael@0: nsCOMPtr mInsertStatement; michael@0: michael@0: UpdateRefcountFunction* mFunction; michael@0: michael@0: nsresult mErrorCode; michael@0: }; michael@0: michael@0: nsresult ProcessValue(mozIStorageValueArray* aValues, michael@0: int32_t aIndex, michael@0: UpdateType aUpdateType); michael@0: michael@0: nsresult CreateJournals(); michael@0: michael@0: nsresult RemoveJournals(const nsTArray& aJournals); michael@0: michael@0: static PLDHashOperator michael@0: DatabaseUpdateCallback(const uint64_t& aKey, michael@0: FileInfoEntry* aValue, michael@0: void* aUserArg); michael@0: michael@0: static PLDHashOperator michael@0: FileInfoUpdateCallback(const uint64_t& aKey, michael@0: FileInfoEntry* aValue, michael@0: void* aUserArg); michael@0: michael@0: static PLDHashOperator michael@0: RollbackSavepointCallback(const uint64_t& aKey, michael@0: FileInfoEntry* aValue, michael@0: void* aUserArg); michael@0: michael@0: FileManager* mFileManager; michael@0: nsClassHashtable mFileInfoEntries; michael@0: nsDataHashtable mSavepointEntriesIndex; michael@0: michael@0: nsTArray mJournalsToCreateBeforeCommit; michael@0: nsTArray mJournalsToRemoveAfterCommit; michael@0: nsTArray mJournalsToRemoveAfterAbort; michael@0: michael@0: bool mInSavepoint; michael@0: }; michael@0: michael@0: END_INDEXEDDB_NAMESPACE michael@0: michael@0: #endif // mozilla_dom_indexeddb_idbtransaction_h__