/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_dom_idbtransaction_h__ #define mozilla_dom_idbtransaction_h__ #include "FlippedOnce.h" #include "mozilla/Attributes.h" #include "mozilla/dom/IDBTransactionBinding.h" #include "mozilla/DOMEventTargetHelper.h" #include "nsCycleCollectionParticipant.h" #include "nsIRunnable.h" #include "nsString.h" #include "nsTArray.h" namespace mozilla { class ErrorResult; class EventChainPreVisitor; namespace dom { class DOMException; class DOMStringList; class IDBCursor; class IDBDatabase; class IDBObjectStore; class IDBOpenDBRequest; class IDBRequest; class StrongWorkerRef; namespace indexedDB { class BackgroundCursorChild; class BackgroundRequestChild; class BackgroundTransactionChild; class BackgroundVersionChangeTransactionChild; class IndexMetadata; class ObjectStoreSpec; class OpenCursorParams; class RequestParams; } // namespace indexedDB class IDBTransaction final : public DOMEventTargetHelper, public nsIRunnable { friend class indexedDB::BackgroundCursorChild; friend class indexedDB::BackgroundRequestChild; public: enum struct Mode { ReadOnly = 0, ReadWrite, ReadWriteFlush, Cleanup, VersionChange, // Only needed for IPC serialization helper, should never be used in code. Invalid }; enum struct ReadyState { Active, Inactive, Committing, Finished }; private: // TODO: Only non-const because of Bug 1575173. RefPtr mDatabase; RefPtr mError; const nsTArray mObjectStoreNames; nsTArray> mObjectStores; nsTArray> mDeletedObjectStores; RefPtr mWorkerRef; nsTArray mCursors; // Tagged with mMode. If mMode is Mode::VersionChange then mBackgroundActor // will be a BackgroundVersionChangeTransactionChild. Otherwise it will be a // BackgroundTransactionChild. union { indexedDB::BackgroundTransactionChild* mNormalBackgroundActor; indexedDB::BackgroundVersionChangeTransactionChild* mVersionChangeBackgroundActor; } mBackgroundActor; const int64_t mLoggingSerialNumber; // Only used for Mode::VersionChange transactions. int64_t mNextObjectStoreId; int64_t mNextIndexId; nsresult mAbortCode; ///< The result that caused the transaction to be ///< aborted, or NS_OK if not aborted. ///< NS_ERROR_DOM_INDEXEDDB_ABORT_ERR indicates that the ///< user explicitly requested aborting. Should be ///< renamed to mResult or so, because it is actually ///< used to check if the transaction has been aborted. uint32_t mPendingRequestCount; ///< Counted via OnNewRequest and ///< OnRequestFinished, so that the ///< transaction can auto-commit when the last ///< pending request finished. const nsString mFilename; const uint32_t mLineNo; const uint32_t mColumn; ReadyState mReadyState = ReadyState::Active; FlippedOnce mStarted; const Mode mMode; bool mCreating; ///< Set between successful creation until the transaction ///< has run on the event-loop. bool mRegistered; ///< Whether mDatabase->RegisterTransaction() has been ///< called (which may not be the case if construction was ///< incomplete). FlippedOnce mAbortedByScript; bool mNotedActiveTransaction; #ifdef DEBUG FlippedOnce mSentCommitOrAbort; FlippedOnce mFiredCompleteOrAbort; #endif public: static MOZ_MUST_USE RefPtr CreateVersionChange( IDBDatabase* aDatabase, indexedDB::BackgroundVersionChangeTransactionChild* aActor, IDBOpenDBRequest* aOpenRequest, int64_t aNextObjectStoreId, int64_t aNextIndexId); static MOZ_MUST_USE RefPtr Create( JSContext* aCx, IDBDatabase* aDatabase, const nsTArray& aObjectStoreNames, Mode aMode); static IDBTransaction* GetCurrent(); void AssertIsOnOwningThread() const #ifdef DEBUG ; #else { } #endif void SetBackgroundActor( indexedDB::BackgroundTransactionChild* aBackgroundActor); void ClearBackgroundActor() { AssertIsOnOwningThread(); if (mMode == Mode::VersionChange) { mBackgroundActor.mVersionChangeBackgroundActor = nullptr; } else { mBackgroundActor.mNormalBackgroundActor = nullptr; } // Note inactive transaction here if we didn't receive the Complete message // from the parent. MaybeNoteInactiveTransaction(); } indexedDB::BackgroundRequestChild* StartRequest( IDBRequest* aRequest, const indexedDB::RequestParams& aParams); void OpenCursor(indexedDB::BackgroundCursorChild* aBackgroundActor, const indexedDB::OpenCursorParams& aParams); void RefreshSpec(bool aMayDelete); bool CanAcceptRequests() const; bool IsCommittingOrFinished() const { AssertIsOnOwningThread(); return mReadyState == ReadyState::Committing || mReadyState == ReadyState::Finished; } bool IsActive() const { AssertIsOnOwningThread(); return mReadyState == ReadyState::Active; } bool IsInactive() const { AssertIsOnOwningThread(); return mReadyState == ReadyState::Inactive; } bool IsFinished() const { AssertIsOnOwningThread(); return mReadyState == ReadyState::Finished; } bool IsWriteAllowed() const { AssertIsOnOwningThread(); return mMode == Mode::ReadWrite || mMode == Mode::ReadWriteFlush || mMode == Mode::Cleanup || mMode == Mode::VersionChange; } bool IsAborted() const { AssertIsOnOwningThread(); return NS_FAILED(mAbortCode); } template class AutoRestoreState { public: explicit AutoRestoreState(IDBTransaction& aOwner) : mOwner { aOwner } #ifdef DEBUG , mSavedPendingRequestCount { mOwner.mPendingRequestCount } #endif { mOwner.AssertIsOnOwningThread(); MOZ_ASSERT(mOwner.mReadyState == OriginalState); mOwner.mReadyState = TemporaryState; } ~AutoRestoreState() { mOwner.AssertIsOnOwningThread(); MOZ_ASSERT(mOwner.mReadyState == TemporaryState); MOZ_ASSERT(mOwner.mPendingRequestCount == mSavedPendingRequestCount); mOwner.mReadyState = OriginalState; } private: IDBTransaction& mOwner; #ifdef DEBUG const uint32_t mSavedPendingRequestCount; #endif }; AutoRestoreState TemporarilyTransitionToActive(); AutoRestoreState TemporarilyTransitionToInactive(); void TransitionToActive() { MOZ_ASSERT(mReadyState == ReadyState::Inactive); mReadyState = ReadyState::Active; } void TransitionToInactive() { MOZ_ASSERT(mReadyState == ReadyState::Active); mReadyState = ReadyState::Inactive; } nsresult AbortCode() const { AssertIsOnOwningThread(); return mAbortCode; } void GetCallerLocation(nsAString& aFilename, uint32_t* aLineNo, uint32_t* aColumn) const; // 'Get' prefix is to avoid name collisions with the enum Mode GetMode() const { AssertIsOnOwningThread(); return mMode; } IDBDatabase* Database() const { AssertIsOnOwningThread(); return mDatabase; } // Only for use by ProfilerHelpers.h const nsTArray& ObjectStoreNamesInternal() const { AssertIsOnOwningThread(); return mObjectStoreNames; } MOZ_MUST_USE RefPtr CreateObjectStore( const indexedDB::ObjectStoreSpec& aSpec); void DeleteObjectStore(int64_t aObjectStoreId); void RenameObjectStore(int64_t aObjectStoreId, const nsAString& aName); void CreateIndex(IDBObjectStore* aObjectStore, const indexedDB::IndexMetadata& aMetadata); void DeleteIndex(IDBObjectStore* aObjectStore, int64_t aIndexId); void RenameIndex(IDBObjectStore* aObjectStore, int64_t aIndexId, const nsAString& aName); void Abort(IDBRequest* aRequest); void Abort(nsresult aErrorCode); int64_t LoggingSerialNumber() const { AssertIsOnOwningThread(); return mLoggingSerialNumber; } nsIGlobalObject* GetParentObject() const; void FireCompleteOrAbortEvents(nsresult aResult); // Only for Mode::VersionChange transactions. int64_t NextObjectStoreId(); // Only for Mode::VersionChange transactions. int64_t NextIndexId(); void InvalidateCursorCaches(); void RegisterCursor(IDBCursor* aCursor); void UnregisterCursor(IDBCursor* aCursor); NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIRUNNABLE NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBTransaction, DOMEventTargetHelper) // nsWrapperCache JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; // Methods bound via WebIDL. IDBDatabase* Db() const { return Database(); } IDBTransactionMode GetMode(ErrorResult& aRv) const; DOMException* GetError() const; MOZ_MUST_USE RefPtr ObjectStore(const nsAString& aName, ErrorResult& aRv); void Commit(ErrorResult& aRv); void Abort(ErrorResult& aRv); IMPL_EVENT_HANDLER(abort) IMPL_EVENT_HANDLER(complete) IMPL_EVENT_HANDLER(error) MOZ_MUST_USE RefPtr ObjectStoreNames() const; // EventTarget void GetEventTargetParent(EventChainPreVisitor& aVisitor) override; private: struct CreatedFromFactoryFunction {}; public: IDBTransaction(IDBDatabase* aDatabase, const nsTArray& aObjectStoreNames, Mode aMode, nsString aFilename, uint32_t aLineNo, uint32_t aColumn, CreatedFromFactoryFunction aDummy); private: ~IDBTransaction(); void AbortInternal(nsresult aAbortCode, RefPtr aError); void SendCommit(); void SendAbort(nsresult aResultCode); void NoteActiveTransaction(); void MaybeNoteInactiveTransaction(); void OnNewRequest(); void OnRequestFinished(bool aRequestCompletedSuccessfully); template auto DoWithTransactionChild(const Func& aFunc) const; bool HasTransactionChild() const; }; } // namespace dom } // namespace mozilla #endif // mozilla_dom_idbtransaction_h__