IDBKeyRange.cpp (7989B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "IDBKeyRange.h" 8 9 #include "Key.h" 10 #include "mozilla/ErrorResult.h" 11 #include "mozilla/HoldDropJSObjects.h" 12 #include "mozilla/dom/BindingUtils.h" 13 #include "mozilla/dom/IDBKeyRangeBinding.h" 14 #include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h" 15 16 namespace mozilla::dom { 17 18 using namespace mozilla::dom::indexedDB; 19 20 namespace { 21 22 void GetKeyFromJSVal(JSContext* aCx, JS::Handle<JS::Value> aVal, Key& aKey, 23 ErrorResult& aRv) { 24 auto result = aKey.SetFromJSVal(aCx, aVal); 25 if (result.isErr()) { 26 aRv = result.unwrapErr().ExtractErrorResult( 27 InvalidMapsTo<NS_ERROR_DOM_INDEXEDDB_DATA_ERR>); 28 return; 29 } 30 31 if (aKey.IsUnset()) { 32 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); 33 } 34 } 35 36 } // namespace 37 38 IDBKeyRange::IDBKeyRange(bool aLowerOpen, bool aUpperOpen, bool aIsOnly) 39 : mCachedLowerVal(JS::UndefinedValue()), 40 mCachedUpperVal(JS::UndefinedValue()), 41 mLowerOpen(aLowerOpen), 42 mUpperOpen(aUpperOpen), 43 mIsOnly(aIsOnly), 44 mHaveCachedLowerVal(false), 45 mHaveCachedUpperVal(false), 46 mRooted(false) { 47 AssertIsOnOwningThread(); 48 } 49 50 IDBKeyRange::~IDBKeyRange() { DropJSObjects(); } 51 52 // static 53 void IDBKeyRange::FromJSVal(JSContext* aCx, JS::Handle<JS::Value> aVal, 54 RefPtr<IDBKeyRange>* aKeyRange, ErrorResult& aRv) { 55 MOZ_ASSERT_IF(!aCx, aVal.isUndefined()); 56 MOZ_ASSERT(aKeyRange); 57 58 RefPtr<IDBKeyRange> keyRange; 59 60 if (aVal.isNullOrUndefined()) { 61 // undefined and null returns no IDBKeyRange. 62 *aKeyRange = std::move(keyRange); 63 return; 64 } 65 66 JS::Rooted<JSObject*> obj(aCx, aVal.isObject() ? &aVal.toObject() : nullptr); 67 68 // Unwrap an IDBKeyRange object if possible. 69 if (obj && NS_SUCCEEDED(UNWRAP_OBJECT(IDBKeyRange, obj, keyRange))) { 70 MOZ_ASSERT(keyRange); 71 *aKeyRange = std::move(keyRange); 72 return; 73 } 74 75 // A valid key returns an 'only' IDBKeyRange. 76 keyRange = new IDBKeyRange(false, false, true); 77 GetKeyFromJSVal(aCx, aVal, keyRange->Lower(), aRv); 78 if (!aRv.Failed()) { 79 *aKeyRange = std::move(keyRange); 80 } 81 } 82 83 // static 84 RefPtr<IDBKeyRange> IDBKeyRange::FromSerialized( 85 const SerializedKeyRange& aKeyRange) { 86 RefPtr<IDBKeyRange> keyRange = new IDBKeyRange( 87 aKeyRange.lowerOpen(), aKeyRange.upperOpen(), aKeyRange.isOnly()); 88 keyRange->Lower() = aKeyRange.lower(); 89 if (!keyRange->IsOnly()) { 90 keyRange->Upper() = aKeyRange.upper(); 91 } 92 return keyRange; 93 } 94 95 void IDBKeyRange::ToSerialized(SerializedKeyRange& aKeyRange) const { 96 aKeyRange.lowerOpen() = LowerOpen(); 97 aKeyRange.upperOpen() = UpperOpen(); 98 aKeyRange.isOnly() = IsOnly(); 99 100 aKeyRange.lower() = Lower(); 101 if (!IsOnly()) { 102 aKeyRange.upper() = Upper(); 103 } 104 } 105 106 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBKeyRange) 107 108 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBKeyRange) 109 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 110 111 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBKeyRange) 112 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedLowerVal) 113 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedUpperVal) 114 NS_IMPL_CYCLE_COLLECTION_TRACE_END 115 116 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBKeyRange) 117 tmp->DropJSObjects(); 118 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 119 120 void IDBKeyRange::DropJSObjects() { 121 if (!mRooted) { 122 return; 123 } 124 mHaveCachedLowerVal = false; 125 mHaveCachedUpperVal = false; 126 mRooted = false; 127 mozilla::DropJSObjects(this); 128 } 129 130 bool IDBKeyRange::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, 131 JS::MutableHandle<JSObject*> aReflector) { 132 return IDBKeyRange_Binding::Wrap(aCx, this, aGivenProto, aReflector); 133 } 134 135 void IDBKeyRange::GetLower(JSContext* aCx, JS::MutableHandle<JS::Value> aResult, 136 ErrorResult& aRv) { 137 AssertIsOnOwningThread(); 138 139 if (!mHaveCachedLowerVal) { 140 if (!mRooted) { 141 mozilla::HoldJSObjects(this); 142 mRooted = true; 143 } 144 145 aRv = Lower().ToJSVal(aCx, mCachedLowerVal); 146 if (aRv.Failed()) { 147 return; 148 } 149 150 mHaveCachedLowerVal = true; 151 } 152 153 aResult.set(mCachedLowerVal); 154 } 155 156 void IDBKeyRange::GetUpper(JSContext* aCx, JS::MutableHandle<JS::Value> aResult, 157 ErrorResult& aRv) { 158 AssertIsOnOwningThread(); 159 160 if (!mHaveCachedUpperVal) { 161 if (!mRooted) { 162 mozilla::HoldJSObjects(this); 163 mRooted = true; 164 } 165 166 aRv = Upper().ToJSVal(aCx, mCachedUpperVal); 167 if (aRv.Failed()) { 168 return; 169 } 170 171 mHaveCachedUpperVal = true; 172 } 173 174 aResult.set(mCachedUpperVal); 175 } 176 177 bool IDBKeyRange::Includes(JSContext* aCx, JS::Handle<JS::Value> aValue, 178 ErrorResult& aRv) const { 179 Key key; 180 GetKeyFromJSVal(aCx, aValue, key, aRv); 181 if (aRv.Failed()) { 182 return false; 183 } 184 185 MOZ_ASSERT(!(Lower().IsUnset() && Upper().IsUnset())); 186 MOZ_ASSERT_IF(IsOnly(), !Lower().IsUnset() && !LowerOpen() && 187 Lower() == Upper() && LowerOpen() == UpperOpen()); 188 189 if (!Lower().IsUnset()) { 190 switch (Key::CompareKeys(Lower(), key)) { 191 case 1: 192 return false; 193 case 0: 194 // Identical keys. 195 return !LowerOpen(); 196 case -1: 197 if (IsOnly()) { 198 return false; 199 } 200 break; 201 default: 202 MOZ_CRASH(); 203 } 204 } 205 206 if (!Upper().IsUnset()) { 207 switch (Key::CompareKeys(key, Upper())) { 208 case 1: 209 return false; 210 case 0: 211 // Identical keys. 212 return !UpperOpen(); 213 case -1: 214 break; 215 } 216 } 217 218 return true; 219 } 220 221 // static 222 RefPtr<IDBKeyRange> IDBKeyRange::Only(const GlobalObject& aGlobal, 223 JS::Handle<JS::Value> aValue, 224 ErrorResult& aRv) { 225 RefPtr<IDBKeyRange> keyRange = new IDBKeyRange(false, false, true); 226 227 GetKeyFromJSVal(aGlobal.Context(), aValue, keyRange->Lower(), aRv); 228 if (aRv.Failed()) { 229 return nullptr; 230 } 231 232 return keyRange; 233 } 234 235 // static 236 RefPtr<IDBKeyRange> IDBKeyRange::LowerBound(const GlobalObject& aGlobal, 237 JS::Handle<JS::Value> aValue, 238 bool aOpen, ErrorResult& aRv) { 239 RefPtr<IDBKeyRange> keyRange = new IDBKeyRange(aOpen, true, false); 240 241 GetKeyFromJSVal(aGlobal.Context(), aValue, keyRange->Lower(), aRv); 242 if (aRv.Failed()) { 243 return nullptr; 244 } 245 246 return keyRange; 247 } 248 249 // static 250 RefPtr<IDBKeyRange> IDBKeyRange::UpperBound(const GlobalObject& aGlobal, 251 JS::Handle<JS::Value> aValue, 252 bool aOpen, ErrorResult& aRv) { 253 RefPtr<IDBKeyRange> keyRange = new IDBKeyRange(true, aOpen, false); 254 255 GetKeyFromJSVal(aGlobal.Context(), aValue, keyRange->Upper(), aRv); 256 if (aRv.Failed()) { 257 return nullptr; 258 } 259 260 return keyRange; 261 } 262 263 // static 264 RefPtr<IDBKeyRange> IDBKeyRange::Bound(const GlobalObject& aGlobal, 265 JS::Handle<JS::Value> aLower, 266 JS::Handle<JS::Value> aUpper, 267 bool aLowerOpen, bool aUpperOpen, 268 ErrorResult& aRv) { 269 RefPtr<IDBKeyRange> keyRange = new IDBKeyRange(aLowerOpen, aUpperOpen, false); 270 271 GetKeyFromJSVal(aGlobal.Context(), aLower, keyRange->Lower(), aRv); 272 if (aRv.Failed()) { 273 return nullptr; 274 } 275 276 GetKeyFromJSVal(aGlobal.Context(), aUpper, keyRange->Upper(), aRv); 277 if (aRv.Failed()) { 278 return nullptr; 279 } 280 281 if (keyRange->Lower() > keyRange->Upper() || 282 (keyRange->Lower() == keyRange->Upper() && (aLowerOpen || aUpperOpen))) { 283 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); 284 return nullptr; 285 } 286 287 return keyRange; 288 } 289 290 } // namespace mozilla::dom