summaryrefslogtreecommitdiffstats
path: root/dom/indexedDB/IDBKeyRange.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/indexedDB/IDBKeyRange.cpp')
-rw-r--r--dom/indexedDB/IDBKeyRange.cpp499
1 files changed, 499 insertions, 0 deletions
diff --git a/dom/indexedDB/IDBKeyRange.cpp b/dom/indexedDB/IDBKeyRange.cpp
new file mode 100644
index 000000000..2de48a70c
--- /dev/null
+++ b/dom/indexedDB/IDBKeyRange.cpp
@@ -0,0 +1,499 @@
+/* -*- 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 "IDBKeyRange.h"
+
+#include "Key.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/IDBKeyRangeBinding.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h"
+
+namespace mozilla {
+namespace dom {
+
+using namespace mozilla::dom::indexedDB;
+
+namespace {
+
+nsresult
+GetKeyFromJSVal(JSContext* aCx,
+ JS::Handle<JS::Value> aVal,
+ Key& aKey)
+{
+ nsresult rv = aKey.SetFromJSVal(aCx, aVal);
+ if (NS_FAILED(rv)) {
+ MOZ_ASSERT(NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_DOM_INDEXEDDB);
+ return rv;
+ }
+
+ if (aKey.IsUnset()) {
+ return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
+ }
+
+ return NS_OK;
+}
+
+} // namespace
+
+IDBKeyRange::IDBKeyRange(nsISupports* aGlobal,
+ bool aLowerOpen,
+ bool aUpperOpen,
+ bool aIsOnly)
+ : mGlobal(aGlobal)
+ , mCachedLowerVal(JS::UndefinedValue())
+ , mCachedUpperVal(JS::UndefinedValue())
+ , mLowerOpen(aLowerOpen)
+ , mUpperOpen(aUpperOpen)
+ , mIsOnly(aIsOnly)
+ , mHaveCachedLowerVal(false)
+ , mHaveCachedUpperVal(false)
+ , mRooted(false)
+{
+#ifdef DEBUG
+ mOwningThread = PR_GetCurrentThread();
+#endif
+ AssertIsOnOwningThread();
+}
+
+IDBKeyRange::~IDBKeyRange()
+{
+ DropJSObjects();
+}
+
+IDBLocaleAwareKeyRange::IDBLocaleAwareKeyRange(nsISupports* aGlobal,
+ bool aLowerOpen,
+ bool aUpperOpen,
+ bool aIsOnly)
+ : IDBKeyRange(aGlobal, aLowerOpen, aUpperOpen, aIsOnly)
+{
+#ifdef DEBUG
+ mOwningThread = PR_GetCurrentThread();
+#endif
+ AssertIsOnOwningThread();
+}
+
+IDBLocaleAwareKeyRange::~IDBLocaleAwareKeyRange()
+{
+ DropJSObjects();
+}
+
+#ifdef DEBUG
+
+void
+IDBKeyRange::AssertIsOnOwningThread() const
+{
+ MOZ_ASSERT(mOwningThread);
+ MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
+}
+
+#endif // DEBUG
+
+// static
+nsresult
+IDBKeyRange::FromJSVal(JSContext* aCx,
+ JS::Handle<JS::Value> aVal,
+ IDBKeyRange** aKeyRange)
+{
+ MOZ_ASSERT_IF(!aCx, aVal.isUndefined());
+
+ RefPtr<IDBKeyRange> keyRange;
+
+ if (aVal.isNullOrUndefined()) {
+ // undefined and null returns no IDBKeyRange.
+ keyRange.forget(aKeyRange);
+ return NS_OK;
+ }
+
+ JS::Rooted<JSObject*> obj(aCx, aVal.isObject() ? &aVal.toObject() : nullptr);
+ bool isValidKey = aVal.isPrimitive();
+ if (!isValidKey) {
+ js::ESClass cls;
+ if (!js::GetBuiltinClass(aCx, obj, &cls)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ isValidKey = cls == js::ESClass::Array || cls == js::ESClass::Date;
+ }
+ if (isValidKey) {
+ // 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
+already_AddRefed<IDBKeyRange>
+IDBKeyRange::FromSerialized(const SerializedKeyRange& aKeyRange)
+{
+ RefPtr<IDBKeyRange> keyRange =
+ new IDBKeyRange(nullptr, aKeyRange.lowerOpen(), aKeyRange.upperOpen(),
+ aKeyRange.isOnly());
+ keyRange->Lower() = aKeyRange.lower();
+ if (!keyRange->IsOnly()) {
+ keyRange->Upper() = aKeyRange.upper();
+ }
+ return keyRange.forget();
+}
+
+void
+IDBKeyRange::ToSerialized(SerializedKeyRange& aKeyRange) const
+{
+ aKeyRange.lowerOpen() = LowerOpen();
+ aKeyRange.upperOpen() = UpperOpen();
+ aKeyRange.isOnly() = IsOnly();
+
+ aKeyRange.lower() = Lower();
+ if (!IsOnly()) {
+ aKeyRange.upper() = Upper();
+ }
+}
+
+void
+IDBKeyRange::GetBindingClause(const nsACString& aKeyColumnName,
+ nsACString& _retval) const
+{
+ NS_NAMED_LITERAL_CSTRING(andStr, " AND ");
+ NS_NAMED_LITERAL_CSTRING(spacecolon, " :");
+ NS_NAMED_LITERAL_CSTRING(lowerKey, "lower_key");
+
+ if (IsOnly()) {
+ // Both keys are set and they're equal.
+ _retval = andStr + aKeyColumnName + NS_LITERAL_CSTRING(" =") +
+ spacecolon + lowerKey;
+ return;
+ }
+
+ nsAutoCString clause;
+
+ if (!Lower().IsUnset()) {
+ // Lower key is set.
+ clause.Append(andStr + aKeyColumnName);
+ clause.AppendLiteral(" >");
+ if (!LowerOpen()) {
+ clause.Append('=');
+ }
+ clause.Append(spacecolon + lowerKey);
+ }
+
+ if (!Upper().IsUnset()) {
+ // Upper key is set.
+ clause.Append(andStr + aKeyColumnName);
+ clause.AppendLiteral(" <");
+ if (!UpperOpen()) {
+ clause.Append('=');
+ }
+ clause.Append(spacecolon + NS_LITERAL_CSTRING("upper_key"));
+ }
+
+ _retval = clause;
+}
+
+nsresult
+IDBKeyRange::BindToStatement(mozIStorageStatement* aStatement) const
+{
+ MOZ_ASSERT(aStatement);
+
+ NS_NAMED_LITERAL_CSTRING(lowerKey, "lower_key");
+
+ if (IsOnly()) {
+ return Lower().BindToStatement(aStatement, lowerKey);
+ }
+
+ nsresult rv;
+
+ if (!Lower().IsUnset()) {
+ rv = Lower().BindToStatement(aStatement, lowerKey);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+
+ if (!Upper().IsUnset()) {
+ rv = Upper().BindToStatement(aStatement, NS_LITERAL_CSTRING("upper_key"));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+
+ return NS_OK;
+}
+
+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_JS_MEMBER_CALLBACK(mCachedLowerVal)
+ NS_IMPL_CYCLE_COLLECTION_TRACE_JS_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)
+
+NS_IMPL_ISUPPORTS_INHERITED0(IDBLocaleAwareKeyRange, IDBKeyRange)
+
+void
+IDBKeyRange::DropJSObjects()
+{
+ if (!mRooted) {
+ return;
+ }
+ mCachedLowerVal.setUndefined();
+ mCachedUpperVal.setUndefined();
+ mHaveCachedLowerVal = false;
+ mHaveCachedUpperVal = false;
+ mRooted = false;
+ mozilla::DropJSObjects(this);
+}
+
+bool
+IDBKeyRange::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aReflector)
+{
+ return IDBKeyRangeBinding::Wrap(aCx, this, aGivenProto, aReflector);
+}
+
+bool
+IDBLocaleAwareKeyRange::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aReflector)
+{
+ return IDBLocaleAwareKeyRangeBinding::Wrap(aCx, this, aGivenProto, aReflector);
+}
+
+void
+IDBKeyRange::GetLower(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
+ ErrorResult& aRv)
+{
+ AssertIsOnOwningThread();
+
+ if (!mHaveCachedLowerVal) {
+ if (!mRooted) {
+ mozilla::HoldJSObjects(this);
+ mRooted = true;
+ }
+
+ aRv = Lower().ToJSVal(aCx, mCachedLowerVal);
+ if (aRv.Failed()) {
+ return;
+ }
+
+ mHaveCachedLowerVal = true;
+ }
+
+ aResult.set(mCachedLowerVal);
+}
+
+void
+IDBKeyRange::GetUpper(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
+ ErrorResult& aRv)
+{
+ AssertIsOnOwningThread();
+
+ if (!mHaveCachedUpperVal) {
+ if (!mRooted) {
+ mozilla::HoldJSObjects(this);
+ mRooted = true;
+ }
+
+ aRv = Upper().ToJSVal(aCx, mCachedUpperVal);
+ if (aRv.Failed()) {
+ return;
+ }
+
+ mHaveCachedUpperVal = true;
+ }
+
+ aResult.set(mCachedUpperVal);
+}
+
+bool
+IDBKeyRange::Includes(JSContext* aCx,
+ JS::Handle<JS::Value> aValue,
+ ErrorResult& aRv) const
+{
+ Key key;
+ aRv = GetKeyFromJSVal(aCx, aValue, key);
+ if (aRv.Failed()) {
+ return false;
+ }
+
+ MOZ_ASSERT(!(Lower().IsUnset() && Upper().IsUnset()));
+ MOZ_ASSERT_IF(IsOnly(),
+ !Lower().IsUnset() && !LowerOpen() &&
+ Lower() == Upper() && LowerOpen() == UpperOpen());
+
+ if (!Lower().IsUnset()) {
+ switch (Key::CompareKeys(Lower(), key)) {
+ case 1:
+ return false;
+ case 0:
+ // Identical keys.
+ return !LowerOpen();
+ case -1:
+ if (IsOnly()) {
+ return false;
+ }
+ break;
+ default:
+ MOZ_CRASH();
+ }
+ }
+
+ if (!Upper().IsUnset()) {
+ switch (Key::CompareKeys(key, Upper())) {
+ case 1:
+ return false;
+ case 0:
+ // Identical keys.
+ return !UpperOpen();
+ case -1:
+ break;
+ }
+ }
+
+ return true;
+}
+
+// static
+already_AddRefed<IDBKeyRange>
+IDBKeyRange::Only(const GlobalObject& aGlobal,
+ JS::Handle<JS::Value> aValue,
+ ErrorResult& aRv)
+{
+ RefPtr<IDBKeyRange> keyRange =
+ new IDBKeyRange(aGlobal.GetAsSupports(), false, false, true);
+
+ aRv = GetKeyFromJSVal(aGlobal.Context(), aValue, keyRange->Lower());
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+
+ return keyRange.forget();
+}
+
+// static
+already_AddRefed<IDBKeyRange>
+IDBKeyRange::LowerBound(const GlobalObject& aGlobal,
+ JS::Handle<JS::Value> aValue,
+ bool aOpen,
+ ErrorResult& aRv)
+{
+ RefPtr<IDBKeyRange> keyRange =
+ new IDBKeyRange(aGlobal.GetAsSupports(), aOpen, true, false);
+
+ aRv = GetKeyFromJSVal(aGlobal.Context(), aValue, keyRange->Lower());
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+
+ return keyRange.forget();
+}
+
+// static
+already_AddRefed<IDBKeyRange>
+IDBKeyRange::UpperBound(const GlobalObject& aGlobal,
+ JS::Handle<JS::Value> aValue,
+ bool aOpen,
+ ErrorResult& aRv)
+{
+ RefPtr<IDBKeyRange> keyRange =
+ new IDBKeyRange(aGlobal.GetAsSupports(), true, aOpen, false);
+
+ aRv = GetKeyFromJSVal(aGlobal.Context(), aValue, keyRange->Upper());
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+
+ return keyRange.forget();
+}
+
+// static
+already_AddRefed<IDBKeyRange>
+IDBKeyRange::Bound(const GlobalObject& aGlobal,
+ JS::Handle<JS::Value> aLower,
+ JS::Handle<JS::Value> aUpper,
+ bool aLowerOpen,
+ bool aUpperOpen,
+ ErrorResult& aRv)
+{
+ RefPtr<IDBKeyRange> keyRange =
+ new IDBKeyRange(aGlobal.GetAsSupports(), aLowerOpen, aUpperOpen, false);
+
+ aRv = GetKeyFromJSVal(aGlobal.Context(), aLower, keyRange->Lower());
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+
+ aRv = GetKeyFromJSVal(aGlobal.Context(), 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();
+}
+
+// static
+already_AddRefed<IDBLocaleAwareKeyRange>
+IDBLocaleAwareKeyRange::Bound(const GlobalObject& aGlobal,
+ JS::Handle<JS::Value> aLower,
+ JS::Handle<JS::Value> aUpper,
+ bool aLowerOpen,
+ bool aUpperOpen,
+ ErrorResult& aRv)
+{
+ RefPtr<IDBLocaleAwareKeyRange> keyRange =
+ new IDBLocaleAwareKeyRange(aGlobal.GetAsSupports(), aLowerOpen, aUpperOpen, false);
+
+ aRv = GetKeyFromJSVal(aGlobal.Context(), aLower, keyRange->Lower());
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+
+ aRv = GetKeyFromJSVal(aGlobal.Context(), aUpper, keyRange->Upper());
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+
+ if (keyRange->Lower() == keyRange->Upper() && (aLowerOpen || aUpperOpen)) {
+ aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
+ return nullptr;
+ }
+
+ return keyRange.forget();
+}
+
+} // namespace dom
+} // namespace mozilla