IDBKeyRange.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "IDBKeyRange.h"
  6. #include "Key.h"
  7. #include "mozilla/ErrorResult.h"
  8. #include "mozilla/dom/BindingUtils.h"
  9. #include "mozilla/dom/IDBKeyRangeBinding.h"
  10. #include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h"
  11. namespace mozilla {
  12. namespace dom {
  13. using namespace mozilla::dom::indexedDB;
  14. namespace {
  15. nsresult
  16. GetKeyFromJSVal(JSContext* aCx,
  17. JS::Handle<JS::Value> aVal,
  18. Key& aKey)
  19. {
  20. nsresult rv = aKey.SetFromJSVal(aCx, aVal, /* aCallGetters */ true);
  21. if (NS_FAILED(rv)) {
  22. MOZ_ASSERT(NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_DOM_INDEXEDDB);
  23. return rv;
  24. }
  25. if (aKey.IsUnset()) {
  26. return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
  27. }
  28. return NS_OK;
  29. }
  30. } // namespace
  31. IDBKeyRange::IDBKeyRange(nsISupports* aGlobal,
  32. bool aLowerOpen,
  33. bool aUpperOpen,
  34. bool aIsOnly)
  35. : mGlobal(aGlobal)
  36. , mCachedLowerVal(JS::UndefinedValue())
  37. , mCachedUpperVal(JS::UndefinedValue())
  38. , mLowerOpen(aLowerOpen)
  39. , mUpperOpen(aUpperOpen)
  40. , mIsOnly(aIsOnly)
  41. , mHaveCachedLowerVal(false)
  42. , mHaveCachedUpperVal(false)
  43. , mRooted(false)
  44. {
  45. #ifdef DEBUG
  46. mOwningThread = PR_GetCurrentThread();
  47. #endif
  48. AssertIsOnOwningThread();
  49. }
  50. IDBKeyRange::~IDBKeyRange()
  51. {
  52. DropJSObjects();
  53. }
  54. IDBLocaleAwareKeyRange::IDBLocaleAwareKeyRange(nsISupports* aGlobal,
  55. bool aLowerOpen,
  56. bool aUpperOpen,
  57. bool aIsOnly)
  58. : IDBKeyRange(aGlobal, aLowerOpen, aUpperOpen, aIsOnly)
  59. {
  60. #ifdef DEBUG
  61. mOwningThread = PR_GetCurrentThread();
  62. #endif
  63. AssertIsOnOwningThread();
  64. }
  65. IDBLocaleAwareKeyRange::~IDBLocaleAwareKeyRange()
  66. {
  67. DropJSObjects();
  68. }
  69. #ifdef DEBUG
  70. void
  71. IDBKeyRange::AssertIsOnOwningThread() const
  72. {
  73. MOZ_ASSERT(mOwningThread);
  74. MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
  75. }
  76. #endif // DEBUG
  77. // static
  78. nsresult
  79. IDBKeyRange::FromJSVal(JSContext* aCx,
  80. JS::Handle<JS::Value> aVal,
  81. IDBKeyRange** aKeyRange)
  82. {
  83. MOZ_ASSERT_IF(!aCx, aVal.isUndefined());
  84. RefPtr<IDBKeyRange> keyRange;
  85. if (aVal.isNullOrUndefined()) {
  86. // undefined and null returns no IDBKeyRange.
  87. keyRange.forget(aKeyRange);
  88. return NS_OK;
  89. }
  90. JS::Rooted<JSObject*> obj(aCx, aVal.isObject() ? &aVal.toObject() : nullptr);
  91. bool isValidKey = aVal.isPrimitive();
  92. if (!isValidKey) {
  93. js::ESClass cls;
  94. if (!js::GetBuiltinClass(aCx, obj, &cls)) {
  95. return NS_ERROR_UNEXPECTED;
  96. }
  97. isValidKey = cls == js::ESClass::Array || cls == js::ESClass::Date;
  98. }
  99. if (isValidKey) {
  100. // A valid key returns an 'only' IDBKeyRange.
  101. keyRange = new IDBKeyRange(nullptr, false, false, true);
  102. nsresult rv = GetKeyFromJSVal(aCx, aVal, keyRange->Lower());
  103. if (NS_FAILED(rv)) {
  104. return rv;
  105. }
  106. }
  107. else {
  108. MOZ_ASSERT(aVal.isObject());
  109. // An object is not permitted unless it's another IDBKeyRange.
  110. if (NS_FAILED(UNWRAP_OBJECT(IDBKeyRange, obj, keyRange))) {
  111. return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
  112. }
  113. }
  114. keyRange.forget(aKeyRange);
  115. return NS_OK;
  116. }
  117. // static
  118. already_AddRefed<IDBKeyRange>
  119. IDBKeyRange::FromSerialized(const SerializedKeyRange& aKeyRange)
  120. {
  121. RefPtr<IDBKeyRange> keyRange =
  122. new IDBKeyRange(nullptr, aKeyRange.lowerOpen(), aKeyRange.upperOpen(),
  123. aKeyRange.isOnly());
  124. keyRange->Lower() = aKeyRange.lower();
  125. if (!keyRange->IsOnly()) {
  126. keyRange->Upper() = aKeyRange.upper();
  127. }
  128. return keyRange.forget();
  129. }
  130. void
  131. IDBKeyRange::ToSerialized(SerializedKeyRange& aKeyRange) const
  132. {
  133. aKeyRange.lowerOpen() = LowerOpen();
  134. aKeyRange.upperOpen() = UpperOpen();
  135. aKeyRange.isOnly() = IsOnly();
  136. aKeyRange.lower() = Lower();
  137. if (!IsOnly()) {
  138. aKeyRange.upper() = Upper();
  139. }
  140. }
  141. void
  142. IDBKeyRange::GetBindingClause(const nsACString& aKeyColumnName,
  143. nsACString& _retval) const
  144. {
  145. NS_NAMED_LITERAL_CSTRING(andStr, " AND ");
  146. NS_NAMED_LITERAL_CSTRING(spacecolon, " :");
  147. NS_NAMED_LITERAL_CSTRING(lowerKey, "lower_key");
  148. if (IsOnly()) {
  149. // Both keys are set and they're equal.
  150. _retval = andStr + aKeyColumnName + NS_LITERAL_CSTRING(" =") +
  151. spacecolon + lowerKey;
  152. return;
  153. }
  154. nsAutoCString clause;
  155. if (!Lower().IsUnset()) {
  156. // Lower key is set.
  157. clause.Append(andStr + aKeyColumnName);
  158. clause.AppendLiteral(" >");
  159. if (!LowerOpen()) {
  160. clause.Append('=');
  161. }
  162. clause.Append(spacecolon + lowerKey);
  163. }
  164. if (!Upper().IsUnset()) {
  165. // Upper key is set.
  166. clause.Append(andStr + aKeyColumnName);
  167. clause.AppendLiteral(" <");
  168. if (!UpperOpen()) {
  169. clause.Append('=');
  170. }
  171. clause.Append(spacecolon + NS_LITERAL_CSTRING("upper_key"));
  172. }
  173. _retval = clause;
  174. }
  175. nsresult
  176. IDBKeyRange::BindToStatement(mozIStorageStatement* aStatement) const
  177. {
  178. MOZ_ASSERT(aStatement);
  179. NS_NAMED_LITERAL_CSTRING(lowerKey, "lower_key");
  180. if (IsOnly()) {
  181. return Lower().BindToStatement(aStatement, lowerKey);
  182. }
  183. nsresult rv;
  184. if (!Lower().IsUnset()) {
  185. rv = Lower().BindToStatement(aStatement, lowerKey);
  186. if (NS_WARN_IF(NS_FAILED(rv))) {
  187. return rv;
  188. }
  189. }
  190. if (!Upper().IsUnset()) {
  191. rv = Upper().BindToStatement(aStatement, NS_LITERAL_CSTRING("upper_key"));
  192. if (NS_WARN_IF(NS_FAILED(rv))) {
  193. return rv;
  194. }
  195. }
  196. return NS_OK;
  197. }
  198. NS_IMPL_CYCLE_COLLECTION_CLASS(IDBKeyRange)
  199. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBKeyRange)
  200. NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
  201. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
  202. NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBKeyRange)
  203. NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedLowerVal)
  204. NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedUpperVal)
  205. NS_IMPL_CYCLE_COLLECTION_TRACE_END
  206. NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBKeyRange)
  207. NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
  208. tmp->DropJSObjects();
  209. NS_IMPL_CYCLE_COLLECTION_UNLINK_END
  210. NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBKeyRange)
  211. NS_INTERFACE_MAP_ENTRY(nsISupports)
  212. NS_INTERFACE_MAP_END
  213. NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBKeyRange)
  214. NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBKeyRange)
  215. NS_IMPL_ISUPPORTS_INHERITED0(IDBLocaleAwareKeyRange, IDBKeyRange)
  216. void
  217. IDBKeyRange::DropJSObjects()
  218. {
  219. if (!mRooted) {
  220. return;
  221. }
  222. mCachedLowerVal.setUndefined();
  223. mCachedUpperVal.setUndefined();
  224. mHaveCachedLowerVal = false;
  225. mHaveCachedUpperVal = false;
  226. mRooted = false;
  227. mozilla::DropJSObjects(this);
  228. }
  229. bool
  230. IDBKeyRange::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aReflector)
  231. {
  232. return IDBKeyRangeBinding::Wrap(aCx, this, aGivenProto, aReflector);
  233. }
  234. bool
  235. IDBLocaleAwareKeyRange::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aReflector)
  236. {
  237. return IDBLocaleAwareKeyRangeBinding::Wrap(aCx, this, aGivenProto, aReflector);
  238. }
  239. void
  240. IDBKeyRange::GetLower(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
  241. ErrorResult& aRv)
  242. {
  243. AssertIsOnOwningThread();
  244. if (!mHaveCachedLowerVal) {
  245. if (!mRooted) {
  246. mozilla::HoldJSObjects(this);
  247. mRooted = true;
  248. }
  249. aRv = Lower().ToJSVal(aCx, mCachedLowerVal);
  250. if (aRv.Failed()) {
  251. return;
  252. }
  253. mHaveCachedLowerVal = true;
  254. }
  255. aResult.set(mCachedLowerVal);
  256. }
  257. void
  258. IDBKeyRange::GetUpper(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
  259. ErrorResult& aRv)
  260. {
  261. AssertIsOnOwningThread();
  262. if (!mHaveCachedUpperVal) {
  263. if (!mRooted) {
  264. mozilla::HoldJSObjects(this);
  265. mRooted = true;
  266. }
  267. aRv = Upper().ToJSVal(aCx, mCachedUpperVal);
  268. if (aRv.Failed()) {
  269. return;
  270. }
  271. mHaveCachedUpperVal = true;
  272. }
  273. aResult.set(mCachedUpperVal);
  274. }
  275. bool
  276. IDBKeyRange::Includes(JSContext* aCx,
  277. JS::Handle<JS::Value> aValue,
  278. ErrorResult& aRv) const
  279. {
  280. Key key;
  281. aRv = GetKeyFromJSVal(aCx, aValue, key);
  282. if (aRv.Failed()) {
  283. return false;
  284. }
  285. MOZ_ASSERT(!(Lower().IsUnset() && Upper().IsUnset()));
  286. MOZ_ASSERT_IF(IsOnly(),
  287. !Lower().IsUnset() && !LowerOpen() &&
  288. Lower() == Upper() && LowerOpen() == UpperOpen());
  289. if (!Lower().IsUnset()) {
  290. switch (Key::CompareKeys(Lower(), key)) {
  291. case 1:
  292. return false;
  293. case 0:
  294. // Identical keys.
  295. return !LowerOpen();
  296. case -1:
  297. if (IsOnly()) {
  298. return false;
  299. }
  300. break;
  301. default:
  302. MOZ_CRASH();
  303. }
  304. }
  305. if (!Upper().IsUnset()) {
  306. switch (Key::CompareKeys(key, Upper())) {
  307. case 1:
  308. return false;
  309. case 0:
  310. // Identical keys.
  311. return !UpperOpen();
  312. case -1:
  313. break;
  314. }
  315. }
  316. return true;
  317. }
  318. // static
  319. already_AddRefed<IDBKeyRange>
  320. IDBKeyRange::Only(const GlobalObject& aGlobal,
  321. JS::Handle<JS::Value> aValue,
  322. ErrorResult& aRv)
  323. {
  324. RefPtr<IDBKeyRange> keyRange =
  325. new IDBKeyRange(aGlobal.GetAsSupports(), false, false, true);
  326. aRv = GetKeyFromJSVal(aGlobal.Context(), aValue, keyRange->Lower());
  327. if (aRv.Failed()) {
  328. return nullptr;
  329. }
  330. return keyRange.forget();
  331. }
  332. // static
  333. already_AddRefed<IDBKeyRange>
  334. IDBKeyRange::LowerBound(const GlobalObject& aGlobal,
  335. JS::Handle<JS::Value> aValue,
  336. bool aOpen,
  337. ErrorResult& aRv)
  338. {
  339. RefPtr<IDBKeyRange> keyRange =
  340. new IDBKeyRange(aGlobal.GetAsSupports(), aOpen, true, false);
  341. aRv = GetKeyFromJSVal(aGlobal.Context(), aValue, keyRange->Lower());
  342. if (aRv.Failed()) {
  343. return nullptr;
  344. }
  345. return keyRange.forget();
  346. }
  347. // static
  348. already_AddRefed<IDBKeyRange>
  349. IDBKeyRange::UpperBound(const GlobalObject& aGlobal,
  350. JS::Handle<JS::Value> aValue,
  351. bool aOpen,
  352. ErrorResult& aRv)
  353. {
  354. RefPtr<IDBKeyRange> keyRange =
  355. new IDBKeyRange(aGlobal.GetAsSupports(), true, aOpen, false);
  356. aRv = GetKeyFromJSVal(aGlobal.Context(), aValue, keyRange->Upper());
  357. if (aRv.Failed()) {
  358. return nullptr;
  359. }
  360. return keyRange.forget();
  361. }
  362. // static
  363. already_AddRefed<IDBKeyRange>
  364. IDBKeyRange::Bound(const GlobalObject& aGlobal,
  365. JS::Handle<JS::Value> aLower,
  366. JS::Handle<JS::Value> aUpper,
  367. bool aLowerOpen,
  368. bool aUpperOpen,
  369. ErrorResult& aRv)
  370. {
  371. RefPtr<IDBKeyRange> keyRange =
  372. new IDBKeyRange(aGlobal.GetAsSupports(), aLowerOpen, aUpperOpen, false);
  373. aRv = GetKeyFromJSVal(aGlobal.Context(), aLower, keyRange->Lower());
  374. if (aRv.Failed()) {
  375. return nullptr;
  376. }
  377. aRv = GetKeyFromJSVal(aGlobal.Context(), aUpper, keyRange->Upper());
  378. if (aRv.Failed()) {
  379. return nullptr;
  380. }
  381. if (keyRange->Lower() > keyRange->Upper() ||
  382. (keyRange->Lower() == keyRange->Upper() && (aLowerOpen || aUpperOpen))) {
  383. aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
  384. return nullptr;
  385. }
  386. return keyRange.forget();
  387. }
  388. // static
  389. already_AddRefed<IDBLocaleAwareKeyRange>
  390. IDBLocaleAwareKeyRange::Bound(const GlobalObject& aGlobal,
  391. JS::Handle<JS::Value> aLower,
  392. JS::Handle<JS::Value> aUpper,
  393. bool aLowerOpen,
  394. bool aUpperOpen,
  395. ErrorResult& aRv)
  396. {
  397. RefPtr<IDBLocaleAwareKeyRange> keyRange =
  398. new IDBLocaleAwareKeyRange(aGlobal.GetAsSupports(), aLowerOpen, aUpperOpen, false);
  399. aRv = GetKeyFromJSVal(aGlobal.Context(), aLower, keyRange->Lower());
  400. if (aRv.Failed()) {
  401. return nullptr;
  402. }
  403. aRv = GetKeyFromJSVal(aGlobal.Context(), aUpper, keyRange->Upper());
  404. if (aRv.Failed()) {
  405. return nullptr;
  406. }
  407. if (keyRange->Lower() == keyRange->Upper() && (aLowerOpen || aUpperOpen)) {
  408. aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
  409. return nullptr;
  410. }
  411. return keyRange.forget();
  412. }
  413. } // namespace dom
  414. } // namespace mozilla