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: #include "IDBRequest.h" michael@0: michael@0: #include "nsIScriptContext.h" michael@0: michael@0: #include "mozilla/ContentEvents.h" michael@0: #include "mozilla/EventDispatcher.h" michael@0: #include "mozilla/dom/ErrorEventBinding.h" michael@0: #include "mozilla/dom/IDBOpenDBRequestBinding.h" michael@0: #include "mozilla/dom/UnionTypes.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsDOMClassInfoID.h" michael@0: #include "nsDOMJSUtils.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsCxPusher.h" michael@0: #include "nsJSUtils.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsString.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsWrapperCacheInlines.h" michael@0: michael@0: #include "AsyncConnectionHelper.h" michael@0: #include "IDBCursor.h" michael@0: #include "IDBEvents.h" michael@0: #include "IDBFactory.h" michael@0: #include "IDBIndex.h" michael@0: #include "IDBObjectStore.h" michael@0: #include "IDBTransaction.h" michael@0: #include "ReportInternalError.h" michael@0: michael@0: namespace { michael@0: michael@0: #ifdef MOZ_ENABLE_PROFILER_SPS michael@0: uint64_t gNextRequestSerialNumber = 1; michael@0: #endif michael@0: michael@0: } // anonymous namespace michael@0: michael@0: USING_INDEXEDDB_NAMESPACE michael@0: using mozilla::dom::OwningIDBObjectStoreOrIDBIndexOrIDBCursor; michael@0: using mozilla::dom::ErrorEventInit; michael@0: using namespace mozilla; michael@0: michael@0: IDBRequest::IDBRequest(IDBDatabase* aDatabase) michael@0: : IDBWrapperCache(aDatabase), michael@0: mResultVal(JSVAL_VOID), michael@0: mActorParent(nullptr), michael@0: #ifdef MOZ_ENABLE_PROFILER_SPS michael@0: mSerialNumber(gNextRequestSerialNumber++), michael@0: #endif michael@0: mErrorCode(NS_OK), michael@0: mLineNo(0), michael@0: mHaveResultOrErrorCode(false) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: } michael@0: michael@0: IDBRequest::IDBRequest(nsPIDOMWindow* aOwner) michael@0: : IDBWrapperCache(aOwner), michael@0: mResultVal(JSVAL_VOID), michael@0: mActorParent(nullptr), michael@0: #ifdef MOZ_ENABLE_PROFILER_SPS michael@0: mSerialNumber(gNextRequestSerialNumber++), michael@0: #endif michael@0: mErrorCode(NS_OK), michael@0: mLineNo(0), michael@0: mHaveResultOrErrorCode(false) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: } michael@0: michael@0: IDBRequest::~IDBRequest() michael@0: { michael@0: mResultVal = JSVAL_VOID; michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: } michael@0: michael@0: // static michael@0: already_AddRefed michael@0: IDBRequest::Create(IDBDatabase* aDatabase, michael@0: IDBTransaction* aTransaction) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: nsRefPtr request(new IDBRequest(aDatabase)); michael@0: michael@0: request->mTransaction = aTransaction; michael@0: request->SetScriptOwner(aDatabase->GetScriptOwner()); michael@0: michael@0: if (!aDatabase->Factory()->FromIPC()) { michael@0: request->CaptureCaller(); michael@0: } michael@0: michael@0: michael@0: return request.forget(); michael@0: } michael@0: michael@0: // static michael@0: already_AddRefed michael@0: IDBRequest::Create(IDBObjectStore* aSourceAsObjectStore, michael@0: IDBDatabase* aDatabase, michael@0: IDBTransaction* aTransaction) michael@0: { michael@0: nsRefPtr request = Create(aDatabase, aTransaction); michael@0: michael@0: request->mSourceAsObjectStore = aSourceAsObjectStore; michael@0: michael@0: return request.forget(); michael@0: } michael@0: michael@0: // static michael@0: already_AddRefed michael@0: IDBRequest::Create(IDBIndex* aSourceAsIndex, michael@0: IDBDatabase* aDatabase, michael@0: IDBTransaction* aTransaction) michael@0: { michael@0: nsRefPtr request = Create(aDatabase, aTransaction); michael@0: michael@0: request->mSourceAsIndex = aSourceAsIndex; michael@0: michael@0: return request.forget(); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: void michael@0: IDBRequest::AssertSourceIsCorrect() const michael@0: { michael@0: // At most one of mSourceAs* is allowed to be non-null. Check that by michael@0: // summing the double negation of each one and asserting the sum is at most michael@0: // 1. michael@0: michael@0: MOZ_ASSERT(!!mSourceAsObjectStore + !!mSourceAsIndex + !!mSourceAsCursor <= 1); michael@0: } michael@0: #endif michael@0: michael@0: void michael@0: IDBRequest::GetSource(Nullable& aSource) const michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: AssertSourceIsCorrect(); michael@0: michael@0: if (mSourceAsObjectStore) { michael@0: aSource.SetValue().SetAsIDBObjectStore() = mSourceAsObjectStore; michael@0: } else if (mSourceAsIndex) { michael@0: aSource.SetValue().SetAsIDBIndex() = mSourceAsIndex; michael@0: } else if (mSourceAsCursor) { michael@0: aSource.SetValue().SetAsIDBCursor() = mSourceAsCursor; michael@0: } else { michael@0: aSource.SetNull(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: IDBRequest::Reset() michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: mResultVal = JSVAL_VOID; michael@0: mHaveResultOrErrorCode = false; michael@0: mError = nullptr; michael@0: } michael@0: michael@0: nsresult michael@0: IDBRequest::NotifyHelperCompleted(HelperBase* aHelper) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: NS_ASSERTION(!mHaveResultOrErrorCode, "Already called!"); michael@0: NS_ASSERTION(JSVAL_IS_VOID(mResultVal), "Should be undefined!"); michael@0: michael@0: mHaveResultOrErrorCode = true; michael@0: michael@0: nsresult rv = aHelper->GetResultCode(); michael@0: michael@0: // If the request failed then set the error code and return. michael@0: if (NS_FAILED(rv)) { michael@0: SetError(rv); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // See if our window is still valid. If not then we're going to pretend that michael@0: // we never completed. michael@0: if (NS_FAILED(CheckInnerWindowCorrectness())) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Otherwise we need to get the result from the helper. michael@0: AutoPushJSContext cx(GetJSContext()); michael@0: if (!cx) { michael@0: IDB_WARNING("Failed to get safe JSContext!"); michael@0: rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; michael@0: SetError(rv); michael@0: return rv; michael@0: } michael@0: michael@0: JS::Rooted global(cx, IDBWrapperCache::GetParentObject()); michael@0: NS_ASSERTION(global, "This should never be null!"); michael@0: michael@0: JSAutoCompartment ac(cx, global); michael@0: AssertIsRooted(); michael@0: michael@0: JS::Rooted value(cx); michael@0: rv = aHelper->GetSuccessResult(cx, &value); michael@0: if (NS_FAILED(rv)) { michael@0: NS_WARNING("GetSuccessResult failed!"); michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mError = nullptr; michael@0: mResultVal = value; michael@0: } michael@0: else { michael@0: SetError(rv); michael@0: mResultVal = JSVAL_VOID; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: void michael@0: IDBRequest::NotifyHelperSentResultsToChildProcess(nsresult aRv) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: NS_ASSERTION(!mHaveResultOrErrorCode, "Already called!"); michael@0: NS_ASSERTION(JSVAL_IS_VOID(mResultVal), "Should be undefined!"); michael@0: michael@0: // See if our window is still valid. If not then we're going to pretend that michael@0: // we never completed. michael@0: if (NS_FAILED(CheckInnerWindowCorrectness())) { michael@0: return; michael@0: } michael@0: michael@0: mHaveResultOrErrorCode = true; michael@0: michael@0: if (NS_FAILED(aRv)) { michael@0: SetError(aRv); michael@0: } michael@0: } michael@0: michael@0: void michael@0: IDBRequest::SetError(nsresult aRv) michael@0: { michael@0: NS_ASSERTION(NS_FAILED(aRv), "Er, what?"); michael@0: NS_ASSERTION(!mError, "Already have an error?"); michael@0: michael@0: mHaveResultOrErrorCode = true; michael@0: mError = new mozilla::dom::DOMError(GetOwner(), aRv); michael@0: mErrorCode = aRv; michael@0: michael@0: mResultVal = JSVAL_VOID; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: nsresult michael@0: IDBRequest::GetErrorCode() const michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: NS_ASSERTION(mHaveResultOrErrorCode, "Don't call me yet!"); michael@0: return mErrorCode; michael@0: } michael@0: #endif michael@0: michael@0: JSContext* michael@0: IDBRequest::GetJSContext() michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: JSContext* cx; michael@0: michael@0: if (GetScriptOwner()) { michael@0: return nsContentUtils::GetSafeJSContext(); michael@0: } michael@0: michael@0: nsresult rv; michael@0: nsIScriptContext* sc = GetContextForEventHandlers(&rv); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: NS_ENSURE_TRUE(sc, nullptr); michael@0: michael@0: cx = sc->GetNativeContext(); michael@0: NS_ASSERTION(cx, "Failed to get a context!"); michael@0: michael@0: return cx; michael@0: } michael@0: michael@0: void michael@0: IDBRequest::CaptureCaller() michael@0: { michael@0: AutoJSContext cx; michael@0: michael@0: const char* filename = nullptr; michael@0: uint32_t lineNo = 0; michael@0: if (!nsJSUtils::GetCallingLocation(cx, &filename, &lineNo)) { michael@0: NS_WARNING("Failed to get caller."); michael@0: return; michael@0: } michael@0: michael@0: mFilename.Assign(NS_ConvertUTF8toUTF16(filename)); michael@0: mLineNo = lineNo; michael@0: } michael@0: michael@0: void michael@0: IDBRequest::FillScriptErrorEvent(ErrorEventInit& aEventInit) const michael@0: { michael@0: aEventInit.mLineno = mLineNo; michael@0: aEventInit.mFilename = mFilename; michael@0: } michael@0: michael@0: mozilla::dom::IDBRequestReadyState michael@0: IDBRequest::ReadyState() const michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: if (IsPending()) { michael@0: return IDBRequestReadyState::Pending; michael@0: } michael@0: michael@0: return IDBRequestReadyState::Done; michael@0: } michael@0: michael@0: JSObject* michael@0: IDBRequest::WrapObject(JSContext* aCx) michael@0: { michael@0: return IDBRequestBinding::Wrap(aCx, this); michael@0: } michael@0: michael@0: void michael@0: IDBRequest::GetResult(JSContext* aCx, JS::MutableHandle aResult, michael@0: ErrorResult& aRv) const michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: if (!mHaveResultOrErrorCode) { michael@0: // XXX Need a real error code here. michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); michael@0: } michael@0: michael@0: JS::ExposeValueToActiveJS(mResultVal); michael@0: aResult.set(mResultVal); michael@0: } michael@0: michael@0: mozilla::dom::DOMError* michael@0: IDBRequest::GetError(mozilla::ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: if (!mHaveResultOrErrorCode) { michael@0: aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: return mError; michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(IDBRequest) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBRequest, IDBWrapperCache) michael@0: // Don't need NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS because michael@0: // DOMEventTargetHelper does it for us. michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceAsObjectStore) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceAsIndex) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceAsCursor) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBRequest, IDBWrapperCache) michael@0: tmp->mResultVal = JSVAL_VOID; michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceAsObjectStore) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceAsIndex) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceAsCursor) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mTransaction) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mError) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(IDBRequest, IDBWrapperCache) michael@0: // Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because michael@0: // DOMEventTargetHelper does it for us. michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mResultVal) michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_END michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBRequest) michael@0: NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache) michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(IDBRequest, IDBWrapperCache) michael@0: NS_IMPL_RELEASE_INHERITED(IDBRequest, IDBWrapperCache) michael@0: michael@0: nsresult michael@0: IDBRequest::PreHandleEvent(EventChainPreVisitor& aVisitor) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: aVisitor.mCanHandle = true; michael@0: aVisitor.mParentTarget = mTransaction; michael@0: return NS_OK; michael@0: } michael@0: michael@0: IDBOpenDBRequest::IDBOpenDBRequest(nsPIDOMWindow* aOwner) michael@0: : IDBRequest(aOwner) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: } michael@0: michael@0: IDBOpenDBRequest::~IDBOpenDBRequest() michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: } michael@0: michael@0: // static michael@0: already_AddRefed michael@0: IDBOpenDBRequest::Create(IDBFactory* aFactory, michael@0: nsPIDOMWindow* aOwner, michael@0: JS::Handle aScriptOwner) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: NS_ASSERTION(aFactory, "Null pointer!"); michael@0: michael@0: nsRefPtr request = new IDBOpenDBRequest(aOwner); michael@0: michael@0: request->SetScriptOwner(aScriptOwner); michael@0: request->mFactory = aFactory; michael@0: michael@0: if (!aFactory->FromIPC()) { michael@0: request->CaptureCaller(); michael@0: } michael@0: michael@0: return request.forget(); michael@0: } michael@0: michael@0: void michael@0: IDBOpenDBRequest::SetTransaction(IDBTransaction* aTransaction) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: NS_ASSERTION(!aTransaction || !mTransaction, michael@0: "Shouldn't have a transaction here!"); michael@0: michael@0: mTransaction = aTransaction; michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(IDBOpenDBRequest) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBOpenDBRequest, michael@0: IDBRequest) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFactory) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBOpenDBRequest, michael@0: IDBRequest) michael@0: // Don't unlink mFactory! michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBOpenDBRequest) michael@0: NS_INTERFACE_MAP_END_INHERITING(IDBRequest) michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(IDBOpenDBRequest, IDBRequest) michael@0: NS_IMPL_RELEASE_INHERITED(IDBOpenDBRequest, IDBRequest) michael@0: michael@0: nsresult michael@0: IDBOpenDBRequest::PostHandleEvent(EventChainPostVisitor& aVisitor) michael@0: { michael@0: return IndexedDatabaseManager::FireWindowOnError(GetOwner(), aVisitor); michael@0: } michael@0: michael@0: JSObject* michael@0: IDBOpenDBRequest::WrapObject(JSContext* aCx) michael@0: { michael@0: return IDBOpenDBRequestBinding::Wrap(aCx, this); michael@0: }