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 "base/basictypes.h" michael@0: michael@0: #include "IDBKeyRange.h" michael@0: michael@0: #include "nsIXPConnect.h" michael@0: michael@0: #include "nsJSUtils.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsDOMClassInfoID.h" michael@0: #include "Key.h" michael@0: michael@0: #include "mozilla/dom/IDBKeyRangeBinding.h" michael@0: #include "mozilla/dom/indexedDB/PIndexedDBIndex.h" michael@0: #include "mozilla/dom/indexedDB/PIndexedDBObjectStore.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: USING_INDEXEDDB_NAMESPACE michael@0: using namespace mozilla::dom::indexedDB::ipc; michael@0: michael@0: namespace { michael@0: michael@0: inline nsresult michael@0: GetKeyFromJSVal(JSContext* aCx, michael@0: JS::Handle aVal, michael@0: Key& aKey, michael@0: bool aAllowUnset = false) michael@0: { michael@0: nsresult rv = aKey.SetFromJSVal(aCx, aVal); michael@0: if (NS_FAILED(rv)) { michael@0: NS_ASSERTION(NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_DOM_INDEXEDDB, michael@0: "Bad error code!"); michael@0: return rv; michael@0: } michael@0: michael@0: if (aKey.IsUnset() && !aAllowUnset) { michael@0: return NS_ERROR_DOM_INDEXEDDB_DATA_ERR; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: } // anonymous namespace michael@0: michael@0: // static michael@0: nsresult michael@0: IDBKeyRange::FromJSVal(JSContext* aCx, michael@0: JS::Handle aVal, michael@0: IDBKeyRange** aKeyRange) michael@0: { michael@0: nsRefPtr keyRange; michael@0: michael@0: if (aVal.isNullOrUndefined()) { michael@0: // undefined and null returns no IDBKeyRange. michael@0: keyRange.forget(aKeyRange); michael@0: return NS_OK; michael@0: } michael@0: michael@0: JS::Rooted obj(aCx, aVal.isObject() ? &aVal.toObject() : nullptr); michael@0: if (aVal.isPrimitive() || JS_IsArrayObject(aCx, obj) || michael@0: JS_ObjectIsDate(aCx, obj)) { michael@0: // A valid key returns an 'only' IDBKeyRange. michael@0: keyRange = new IDBKeyRange(nullptr, false, false, true); michael@0: michael@0: nsresult rv = GetKeyFromJSVal(aCx, aVal, keyRange->Lower()); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: } michael@0: else { michael@0: MOZ_ASSERT(aVal.isObject()); michael@0: // An object is not permitted unless it's another IDBKeyRange. michael@0: if (NS_FAILED(UNWRAP_OBJECT(IDBKeyRange, obj, keyRange))) { michael@0: return NS_ERROR_DOM_INDEXEDDB_DATA_ERR; michael@0: } michael@0: } michael@0: michael@0: keyRange.forget(aKeyRange); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // static michael@0: template michael@0: already_AddRefed michael@0: IDBKeyRange::FromSerializedKeyRange(const T& aKeyRange) michael@0: { michael@0: nsRefPtr keyRange = michael@0: new IDBKeyRange(nullptr, aKeyRange.lowerOpen(), aKeyRange.upperOpen(), michael@0: aKeyRange.isOnly()); michael@0: keyRange->Lower() = aKeyRange.lower(); michael@0: if (!keyRange->IsOnly()) { michael@0: keyRange->Upper() = aKeyRange.upper(); michael@0: } michael@0: return keyRange.forget(); michael@0: } michael@0: michael@0: template michael@0: void michael@0: IDBKeyRange::ToSerializedKeyRange(T& aKeyRange) michael@0: { michael@0: aKeyRange.lowerOpen() = IsLowerOpen(); michael@0: aKeyRange.upperOpen() = IsUpperOpen(); michael@0: aKeyRange.isOnly() = IsOnly(); michael@0: michael@0: aKeyRange.lower() = Lower(); michael@0: if (!IsOnly()) { michael@0: aKeyRange.upper() = Upper(); michael@0: } michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(IDBKeyRange) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBKeyRange) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBKeyRange) michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedLowerVal) michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedUpperVal) michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBKeyRange) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal) michael@0: tmp->DropJSObjects(); michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBKeyRange) michael@0: NS_INTERFACE_MAP_ENTRY(nsISupports) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBKeyRange) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBKeyRange) michael@0: michael@0: void michael@0: IDBKeyRange::DropJSObjects() michael@0: { michael@0: if (!mRooted) { michael@0: return; michael@0: } michael@0: mCachedLowerVal = JS::UndefinedValue(); michael@0: mCachedUpperVal = JS::UndefinedValue(); michael@0: mHaveCachedLowerVal = false; michael@0: mHaveCachedUpperVal = false; michael@0: mRooted = false; michael@0: mozilla::DropJSObjects(this); michael@0: } michael@0: michael@0: IDBKeyRange::~IDBKeyRange() michael@0: { michael@0: DropJSObjects(); michael@0: } michael@0: michael@0: JSObject* michael@0: IDBKeyRange::WrapObject(JSContext* aCx) michael@0: { michael@0: return IDBKeyRangeBinding::Wrap(aCx, this); michael@0: } michael@0: michael@0: void michael@0: IDBKeyRange::GetLower(JSContext* aCx, JS::MutableHandle aResult, michael@0: ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: if (!mHaveCachedLowerVal) { michael@0: if (!mRooted) { michael@0: mozilla::HoldJSObjects(this); michael@0: mRooted = true; michael@0: } michael@0: michael@0: aRv = Lower().ToJSVal(aCx, mCachedLowerVal); michael@0: if (aRv.Failed()) { michael@0: return; michael@0: } michael@0: michael@0: mHaveCachedLowerVal = true; michael@0: } michael@0: michael@0: JS::ExposeValueToActiveJS(mCachedLowerVal); michael@0: aResult.set(mCachedLowerVal); michael@0: } michael@0: michael@0: void michael@0: IDBKeyRange::GetUpper(JSContext* aCx, JS::MutableHandle aResult, michael@0: ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: if (!mHaveCachedUpperVal) { michael@0: if (!mRooted) { michael@0: mozilla::HoldJSObjects(this); michael@0: mRooted = true; michael@0: } michael@0: michael@0: aRv = Upper().ToJSVal(aCx, mCachedUpperVal); michael@0: if (aRv.Failed()) { michael@0: return; michael@0: } michael@0: michael@0: mHaveCachedUpperVal = true; michael@0: } michael@0: michael@0: JS::ExposeValueToActiveJS(mCachedUpperVal); michael@0: aResult.set(mCachedUpperVal); michael@0: } michael@0: michael@0: // static michael@0: already_AddRefed michael@0: IDBKeyRange::Only(const GlobalObject& aGlobal, JSContext* aCx, michael@0: JS::Handle aValue, ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: nsRefPtr keyRange = michael@0: new IDBKeyRange(aGlobal.GetAsSupports(), false, false, true); michael@0: michael@0: aRv = GetKeyFromJSVal(aCx, aValue, keyRange->Lower()); michael@0: if (aRv.Failed()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return keyRange.forget(); michael@0: } michael@0: michael@0: // static michael@0: already_AddRefed michael@0: IDBKeyRange::LowerBound(const GlobalObject& aGlobal, JSContext* aCx, michael@0: JS::Handle aValue, bool aOpen, michael@0: ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: nsRefPtr keyRange = michael@0: new IDBKeyRange(aGlobal.GetAsSupports(), aOpen, true, false); michael@0: michael@0: aRv = GetKeyFromJSVal(aCx, aValue, keyRange->Lower()); michael@0: if (aRv.Failed()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return keyRange.forget(); michael@0: } michael@0: michael@0: // static michael@0: already_AddRefed michael@0: IDBKeyRange::UpperBound(const GlobalObject& aGlobal, JSContext* aCx, michael@0: JS::Handle aValue, bool aOpen, michael@0: ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: nsRefPtr keyRange = michael@0: new IDBKeyRange(aGlobal.GetAsSupports(), true, aOpen, false); michael@0: michael@0: aRv = GetKeyFromJSVal(aCx, aValue, keyRange->Upper()); michael@0: if (aRv.Failed()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return keyRange.forget(); michael@0: } michael@0: michael@0: // static michael@0: already_AddRefed michael@0: IDBKeyRange::Bound(const GlobalObject& aGlobal, JSContext* aCx, michael@0: JS::Handle aLower, JS::Handle aUpper, michael@0: bool aLowerOpen, bool aUpperOpen, ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: nsRefPtr keyRange = michael@0: new IDBKeyRange(aGlobal.GetAsSupports(), aLowerOpen, aUpperOpen, false); michael@0: michael@0: aRv = GetKeyFromJSVal(aCx, aLower, keyRange->Lower()); michael@0: if (aRv.Failed()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: aRv = GetKeyFromJSVal(aCx, aUpper, keyRange->Upper()); michael@0: if (aRv.Failed()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (keyRange->Lower() > keyRange->Upper() || michael@0: (keyRange->Lower() == keyRange->Upper() && (aLowerOpen || aUpperOpen))) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: return keyRange.forget(); michael@0: } michael@0: michael@0: // Explicitly instantiate for all our key range types... Grumble. michael@0: template already_AddRefed michael@0: IDBKeyRange::FromSerializedKeyRange (const KeyRange& aKeyRange); michael@0: michael@0: template void michael@0: IDBKeyRange::ToSerializedKeyRange (KeyRange& aKeyRange);