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