summaryrefslogtreecommitdiffstats
path: root/dom/indexedDB/IDBRequest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/indexedDB/IDBRequest.cpp')
-rw-r--r--dom/indexedDB/IDBRequest.cpp449
1 files changed, 449 insertions, 0 deletions
diff --git a/dom/indexedDB/IDBRequest.cpp b/dom/indexedDB/IDBRequest.cpp
new file mode 100644
index 0000000000..3e773d2444
--- /dev/null
+++ b/dom/indexedDB/IDBRequest.cpp
@@ -0,0 +1,449 @@
+/* -*- 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/. */
+
+#include "IDBRequest.h"
+
+#include <utility>
+
+#include "BackgroundChildImpl.h"
+#include "IDBCursor.h"
+#include "IDBDatabase.h"
+#include "IDBEvents.h"
+#include "IDBFactory.h"
+#include "IDBIndex.h"
+#include "IDBObjectStore.h"
+#include "IDBTransaction.h"
+#include "IndexedDatabaseManager.h"
+#include "ReportInternalError.h"
+#include "mozilla/ContentEvents.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/EventDispatcher.h"
+#include "mozilla/HoldDropJSObjects.h"
+#include "mozilla/dom/DOMException.h"
+#include "mozilla/dom/ErrorEventBinding.h"
+#include "mozilla/dom/IDBOpenDBRequestBinding.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/dom/WorkerRef.h"
+#include "nsCOMPtr.h"
+#include "nsContentUtils.h"
+#include "nsIGlobalObject.h"
+#include "nsIScriptContext.h"
+#include "nsJSUtils.h"
+#include "nsString.h"
+#include "ThreadLocal.h"
+
+namespace mozilla::dom {
+
+using namespace mozilla::dom::indexedDB;
+using namespace mozilla::ipc;
+
+IDBRequest::IDBRequest(IDBDatabase* aDatabase)
+ : DOMEventTargetHelper(aDatabase),
+ mLoggingSerialNumber(0),
+ mLineNo(0),
+ mColumn(0),
+ mHaveResultOrErrorCode(false) {
+ MOZ_ASSERT(aDatabase);
+ aDatabase->AssertIsOnOwningThread();
+
+ InitMembers();
+}
+
+IDBRequest::IDBRequest(nsIGlobalObject* aGlobal)
+ : DOMEventTargetHelper(aGlobal),
+ mLoggingSerialNumber(0),
+ mLineNo(0),
+ mColumn(0),
+ mHaveResultOrErrorCode(false) {
+ InitMembers();
+}
+
+IDBRequest::~IDBRequest() {
+ AssertIsOnOwningThread();
+ mozilla::DropJSObjects(this);
+}
+
+void IDBRequest::InitMembers() {
+ AssertIsOnOwningThread();
+
+ mResultVal.setUndefined();
+ mLoggingSerialNumber = NextSerialNumber();
+ mErrorCode = NS_OK;
+ mLineNo = 0;
+ mColumn = 0;
+ mHaveResultOrErrorCode = false;
+}
+
+// static
+MovingNotNull<RefPtr<IDBRequest>> IDBRequest::Create(
+ JSContext* aCx, IDBDatabase* aDatabase,
+ SafeRefPtr<IDBTransaction> aTransaction) {
+ MOZ_ASSERT(aCx);
+ MOZ_ASSERT(aDatabase);
+ aDatabase->AssertIsOnOwningThread();
+
+ RefPtr<IDBRequest> request = new IDBRequest(aDatabase);
+ CaptureCaller(aCx, request->mFilename, &request->mLineNo, &request->mColumn);
+
+ request->mTransaction = std::move(aTransaction);
+
+ return WrapMovingNotNullUnchecked(std::move(request));
+}
+
+// static
+MovingNotNull<RefPtr<IDBRequest>> IDBRequest::Create(
+ JSContext* aCx, IDBObjectStore* aSourceAsObjectStore,
+ IDBDatabase* aDatabase, SafeRefPtr<IDBTransaction> aTransaction) {
+ MOZ_ASSERT(aSourceAsObjectStore);
+ aSourceAsObjectStore->AssertIsOnOwningThread();
+
+ auto request =
+ Create(aCx, aDatabase, std::move(aTransaction)).unwrapBasePtr();
+
+ request->mSourceAsObjectStore = aSourceAsObjectStore;
+
+ return WrapMovingNotNullUnchecked(std::move(request));
+}
+
+// static
+MovingNotNull<RefPtr<IDBRequest>> IDBRequest::Create(
+ JSContext* aCx, IDBIndex* aSourceAsIndex, IDBDatabase* aDatabase,
+ SafeRefPtr<IDBTransaction> aTransaction) {
+ MOZ_ASSERT(aSourceAsIndex);
+ aSourceAsIndex->AssertIsOnOwningThread();
+
+ auto request =
+ Create(aCx, aDatabase, std::move(aTransaction)).unwrapBasePtr();
+
+ request->mSourceAsIndex = aSourceAsIndex;
+
+ return WrapMovingNotNullUnchecked(std::move(request));
+}
+
+// static
+uint64_t IDBRequest::NextSerialNumber() {
+ BackgroundChildImpl::ThreadLocal* threadLocal =
+ BackgroundChildImpl::GetThreadLocalForCurrentThread();
+ MOZ_ASSERT(threadLocal);
+
+ const auto& idbThreadLocal = threadLocal->mIndexedDBThreadLocal;
+ MOZ_ASSERT(idbThreadLocal);
+
+ return idbThreadLocal->NextRequestSN();
+}
+
+void IDBRequest::SetLoggingSerialNumber(uint64_t aLoggingSerialNumber) {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aLoggingSerialNumber > mLoggingSerialNumber);
+
+ mLoggingSerialNumber = aLoggingSerialNumber;
+}
+
+void IDBRequest::CaptureCaller(JSContext* aCx, nsAString& aFilename,
+ uint32_t* aLineNo, uint32_t* aColumn) {
+ MOZ_ASSERT(aFilename.IsEmpty());
+ MOZ_ASSERT(aLineNo);
+ MOZ_ASSERT(aColumn);
+
+ nsJSUtils::GetCallingLocation(aCx, aFilename, aLineNo, aColumn);
+}
+
+void IDBRequest::GetSource(
+ Nullable<OwningIDBObjectStoreOrIDBIndexOrIDBCursor>& aSource) const {
+ AssertIsOnOwningThread();
+
+ MOZ_ASSERT_IF(mSourceAsObjectStore, !mSourceAsIndex);
+ MOZ_ASSERT_IF(mSourceAsIndex, !mSourceAsObjectStore);
+ MOZ_ASSERT_IF(mSourceAsCursor, mSourceAsObjectStore || mSourceAsIndex);
+
+ // Always check cursor first since cursor requests hold both the cursor and
+ // the objectStore or index the cursor came from.
+ if (mSourceAsCursor) {
+ aSource.SetValue().SetAsIDBCursor() = mSourceAsCursor;
+ } else if (mSourceAsObjectStore) {
+ aSource.SetValue().SetAsIDBObjectStore() = mSourceAsObjectStore;
+ } else if (mSourceAsIndex) {
+ aSource.SetValue().SetAsIDBIndex() = mSourceAsIndex;
+ } else {
+ aSource.SetNull();
+ }
+}
+
+void IDBRequest::Reset() {
+ AssertIsOnOwningThread();
+
+ mResultVal.setUndefined();
+
+ mHaveResultOrErrorCode = false;
+ mError = nullptr;
+}
+
+void IDBRequest::SetError(nsresult aRv) {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(NS_FAILED(aRv));
+ MOZ_ASSERT(NS_ERROR_GET_MODULE(aRv) == NS_ERROR_MODULE_DOM_INDEXEDDB);
+ MOZ_ASSERT(!mError);
+
+ mHaveResultOrErrorCode = true;
+ mError = DOMException::Create(aRv);
+ mErrorCode = aRv;
+
+ mResultVal.setUndefined();
+}
+
+#ifdef DEBUG
+
+nsresult IDBRequest::GetErrorCode() const {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mHaveResultOrErrorCode);
+
+ return mErrorCode;
+}
+
+DOMException* IDBRequest::GetErrorAfterResult() const {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mHaveResultOrErrorCode);
+
+ return mError;
+}
+
+#endif // DEBUG
+
+void IDBRequest::GetCallerLocation(nsAString& aFilename, uint32_t* aLineNo,
+ uint32_t* aColumn) const {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aLineNo);
+ MOZ_ASSERT(aColumn);
+
+ aFilename = mFilename;
+ *aLineNo = mLineNo;
+ *aColumn = mColumn;
+}
+
+IDBRequestReadyState IDBRequest::ReadyState() const {
+ AssertIsOnOwningThread();
+
+ return IsPending() ? IDBRequestReadyState::Pending
+ : IDBRequestReadyState::Done;
+}
+
+void IDBRequest::SetSource(IDBCursor* aSource) {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aSource);
+ MOZ_ASSERT(mSourceAsObjectStore || mSourceAsIndex);
+ MOZ_ASSERT(!mSourceAsCursor);
+
+ mSourceAsCursor = aSource;
+}
+
+JSObject* IDBRequest::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) {
+ return IDBRequest_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+void IDBRequest::GetResult(JS::MutableHandle<JS::Value> aResult,
+ ErrorResult& aRv) const {
+ AssertIsOnOwningThread();
+
+ if (!mHaveResultOrErrorCode) {
+ aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return;
+ }
+
+ aResult.set(mResultVal);
+}
+
+DOMException* IDBRequest::GetError(ErrorResult& aRv) {
+ AssertIsOnOwningThread();
+
+ if (!mHaveResultOrErrorCode) {
+ aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return nullptr;
+ }
+
+ return mError;
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(IDBRequest)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBRequest,
+ DOMEventTargetHelper)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceAsObjectStore)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceAsIndex)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceAsCursor)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBRequest,
+ DOMEventTargetHelper)
+ mozilla::DropJSObjects(tmp);
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceAsObjectStore)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceAsIndex)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceAsCursor)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mTransaction)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mError)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(IDBRequest, DOMEventTargetHelper)
+ // Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because
+ // DOMEventTargetHelper does it for us.
+ NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultVal)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBRequest)
+ if (aIID.Equals(NS_GET_IID(mozilla::dom::detail::PrivateIDBRequest))) {
+ foundInterface = this;
+ } else
+NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
+
+NS_IMPL_ADDREF_INHERITED(IDBRequest, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(IDBRequest, DOMEventTargetHelper)
+
+void IDBRequest::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
+ AssertIsOnOwningThread();
+
+ aVisitor.mCanHandle = true;
+ aVisitor.SetParentTarget(mTransaction.unsafeGetRawPtr(), false);
+}
+
+IDBOpenDBRequest::IDBOpenDBRequest(SafeRefPtr<IDBFactory> aFactory,
+ nsIGlobalObject* aGlobal,
+ bool aFileHandleDisabled)
+ : IDBRequest(aGlobal),
+ mFactory(std::move(aFactory)),
+ mFileHandleDisabled(aFileHandleDisabled),
+ mIncreasedActiveDatabaseCount(false) {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mFactory);
+ MOZ_ASSERT(aGlobal);
+}
+
+IDBOpenDBRequest::~IDBOpenDBRequest() {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(!mIncreasedActiveDatabaseCount);
+}
+
+// static
+RefPtr<IDBOpenDBRequest> IDBOpenDBRequest::Create(
+ JSContext* aCx, SafeRefPtr<IDBFactory> aFactory, nsIGlobalObject* aGlobal) {
+ MOZ_ASSERT(aFactory);
+ aFactory->AssertIsOnOwningThread();
+ MOZ_ASSERT(aGlobal);
+
+ bool fileHandleDisabled = !StaticPrefs::dom_fileHandle_enabled();
+
+ RefPtr<IDBOpenDBRequest> request =
+ new IDBOpenDBRequest(std::move(aFactory), aGlobal, fileHandleDisabled);
+ CaptureCaller(aCx, request->mFilename, &request->mLineNo, &request->mColumn);
+
+ if (!NS_IsMainThread()) {
+ WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+ MOZ_ASSERT(workerPrivate);
+
+ workerPrivate->AssertIsOnWorkerThread();
+
+ request->mWorkerRef =
+ StrongWorkerRef::Create(workerPrivate, "IDBOpenDBRequest");
+ if (NS_WARN_IF(!request->mWorkerRef)) {
+ return nullptr;
+ }
+ }
+
+ request->IncreaseActiveDatabaseCount();
+
+ return request;
+}
+
+void IDBOpenDBRequest::SetTransaction(SafeRefPtr<IDBTransaction> aTransaction) {
+ AssertIsOnOwningThread();
+
+ MOZ_ASSERT(!aTransaction || !mTransaction);
+
+ mTransaction = std::move(aTransaction);
+}
+
+void IDBOpenDBRequest::DispatchNonTransactionError(nsresult aErrorCode) {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(NS_FAILED(aErrorCode));
+ MOZ_ASSERT(NS_ERROR_GET_MODULE(aErrorCode) == NS_ERROR_MODULE_DOM_INDEXEDDB);
+
+ // The actor failed to initiate, decrease the number of active IDBOpenRequests
+ // here since NoteComplete won't be called.
+ MaybeDecreaseActiveDatabaseCount();
+
+ SetError(aErrorCode);
+
+ // Make an error event and fire it at the target.
+ auto event = CreateGenericEvent(this, nsDependentString(kErrorEventType),
+ eDoesBubble, eCancelable);
+
+ IgnoredErrorResult rv;
+ DispatchEvent(*event, rv);
+ if (rv.Failed()) {
+ NS_WARNING("Failed to dispatch event!");
+ }
+}
+
+void IDBOpenDBRequest::NoteComplete() {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT_IF(!NS_IsMainThread(), mWorkerRef);
+
+ // Normally, we decrease the number of active IDBOpenRequests here.
+ MaybeDecreaseActiveDatabaseCount();
+
+ // If we have a WorkerRef, then nulling this out will release the worker.
+ mWorkerRef = nullptr;
+}
+
+void IDBOpenDBRequest::IncreaseActiveDatabaseCount() {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(!mIncreasedActiveDatabaseCount);
+
+ // Increase the number of active IDBOpenRequests.
+ // Note: We count here instead of the actor's ctor because the preemption
+ // could happen at next JS interrupt but its BackgroundFactoryRequestChild
+ // could be created asynchronously from IDBFactory::BackgroundCreateCallback
+ // ::ActorCreated() if its PBackgroundChild is not created yet on this thread.
+ mFactory->UpdateActiveDatabaseCount(1);
+ mIncreasedActiveDatabaseCount = true;
+}
+
+void IDBOpenDBRequest::MaybeDecreaseActiveDatabaseCount() {
+ AssertIsOnOwningThread();
+
+ if (mIncreasedActiveDatabaseCount) {
+ // Decrease the number of active IDBOpenRequests.
+ mFactory->UpdateActiveDatabaseCount(-1);
+ mIncreasedActiveDatabaseCount = false;
+ }
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(IDBOpenDBRequest)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBOpenDBRequest, IDBRequest)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFactory)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBOpenDBRequest, IDBRequest)
+ // Don't unlink mFactory!
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBOpenDBRequest)
+NS_INTERFACE_MAP_END_INHERITING(IDBRequest)
+
+NS_IMPL_ADDREF_INHERITED(IDBOpenDBRequest, IDBRequest)
+NS_IMPL_RELEASE_INHERITED(IDBOpenDBRequest, IDBRequest)
+
+JSObject* IDBOpenDBRequest::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) {
+ AssertIsOnOwningThread();
+
+ return IDBOpenDBRequest_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+} // namespace mozilla::dom