1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=2 et sw=2 tw=80: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Indexed Database.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * The Mozilla Foundation.
20 : * Portions created by the Initial Developer are Copyright (C) 2010
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Ben Turner <bent.mozilla@gmail.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either the GNU General Public License Version 2 or later (the "GPL"), or
28 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "IDBKeyRange.h"
41 :
42 : #include "nsIXPConnect.h"
43 :
44 : #include "nsDOMClassInfo.h"
45 : #include "nsJSUtils.h"
46 : #include "nsThreadUtils.h"
47 : #include "nsContentUtils.h"
48 :
49 : #include "Key.h"
50 :
51 : USING_INDEXEDDB_NAMESPACE
52 :
53 : namespace {
54 :
55 : inline
56 : bool
57 142 : ReturnKeyRange(JSContext* aCx,
58 : jsval* aVp,
59 : IDBKeyRange* aKeyRange)
60 : {
61 142 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
62 142 : NS_ASSERTION(aCx, "Null pointer!");
63 142 : NS_ASSERTION(aVp, "Null pointer!");
64 142 : NS_ASSERTION(aKeyRange, "Null pointer!");
65 :
66 142 : nsIXPConnect* xpc = nsContentUtils::XPConnect();
67 142 : NS_ASSERTION(xpc, "This should never be null!");
68 :
69 142 : JSObject* global = JS_GetGlobalForScopeChain(aCx);
70 142 : if (!global) {
71 0 : NS_WARNING("Couldn't get global object!");
72 0 : return false;
73 : }
74 :
75 284 : nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
76 142 : if (NS_FAILED(xpc->WrapNative(aCx, global, aKeyRange,
77 : NS_GET_IID(nsIIDBKeyRange),
78 : getter_AddRefs(holder)))) {
79 0 : JS_ReportError(aCx, "Couldn't wrap IDBKeyRange object.");
80 0 : return false;
81 : }
82 :
83 : JSObject* result;
84 142 : if (NS_FAILED(holder->GetJSObject(&result))) {
85 0 : JS_ReportError(aCx, "Couldn't get JSObject from wrapper.");
86 0 : return false;
87 : }
88 :
89 142 : JS_SET_RVAL(aCx, aVp, OBJECT_TO_JSVAL(result));
90 142 : return true;
91 : }
92 :
93 : inline
94 : nsresult
95 428 : GetKeyFromJSVal(JSContext* aCx,
96 : jsval aVal,
97 : Key& aKey,
98 : bool aAllowUnset = false)
99 : {
100 428 : nsresult rv = aKey.SetFromJSVal(aCx, aVal);
101 428 : if (NS_FAILED(rv)) {
102 0 : NS_ASSERTION(NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_DOM_INDEXEDDB,
103 : "Bad error code!");
104 0 : return rv;
105 : }
106 :
107 428 : if (aKey.IsUnset() && !aAllowUnset) {
108 0 : return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
109 : }
110 :
111 428 : return NS_OK;
112 : }
113 :
114 : inline
115 : void
116 3 : ThrowException(JSContext* aCx,
117 : nsresult aErrorCode)
118 : {
119 3 : NS_ASSERTION(NS_FAILED(aErrorCode), "Not an error code!");
120 3 : if (!JS_IsExceptionPending(aCx)) {
121 3 : nsDOMClassInfo::ThrowJSException(aCx, aErrorCode);
122 : }
123 3 : }
124 :
125 : inline
126 : bool
127 199 : GetKeyFromJSValOrThrow(JSContext* aCx,
128 : jsval aVal,
129 : Key& aKey)
130 : {
131 199 : nsresult rv = GetKeyFromJSVal(aCx, aVal, aKey);
132 199 : if (NS_FAILED(rv)) {
133 0 : ThrowException(aCx, rv);
134 0 : return false;
135 : }
136 199 : return true;
137 : }
138 :
139 : JSBool
140 27 : MakeOnlyKeyRange(JSContext* aCx,
141 : unsigned aArgc,
142 : jsval* aVp)
143 : {
144 27 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
145 :
146 : jsval val;
147 27 : if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", &val)) {
148 0 : return false;
149 : }
150 :
151 54 : nsRefPtr<IDBKeyRange> keyRange = new IDBKeyRange(false, false, true);
152 :
153 27 : if (!GetKeyFromJSValOrThrow(aCx, val, keyRange->Lower())) {
154 0 : return false;
155 : }
156 :
157 27 : return ReturnKeyRange(aCx, aVp, keyRange);
158 : }
159 :
160 : JSBool
161 36 : MakeLowerBoundKeyRange(JSContext* aCx,
162 : unsigned aArgc,
163 : jsval* aVp)
164 : {
165 36 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
166 :
167 : jsval val;
168 36 : JSBool open = false;
169 36 : if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v/b", &val, &open)) {
170 0 : return false;
171 : }
172 :
173 72 : nsRefPtr<IDBKeyRange> keyRange = new IDBKeyRange(open, true, false);
174 :
175 36 : if (!GetKeyFromJSValOrThrow(aCx, val, keyRange->Lower())) {
176 0 : return false;
177 : }
178 :
179 36 : return ReturnKeyRange(aCx, aVp, keyRange);
180 : }
181 :
182 : JSBool
183 28 : MakeUpperBoundKeyRange(JSContext* aCx,
184 : unsigned aArgc,
185 : jsval* aVp)
186 : {
187 28 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
188 :
189 : jsval val;
190 28 : JSBool open = false;
191 28 : if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v/b", &val, &open)) {
192 0 : return false;
193 : }
194 :
195 56 : nsRefPtr<IDBKeyRange> keyRange = new IDBKeyRange(true, open, false);
196 :
197 28 : if (!GetKeyFromJSValOrThrow(aCx, val, keyRange->Upper())) {
198 0 : return false;
199 : }
200 :
201 28 : return ReturnKeyRange(aCx, aVp, keyRange);
202 : }
203 :
204 : JSBool
205 54 : MakeBoundKeyRange(JSContext* aCx,
206 : unsigned aArgc,
207 : jsval* aVp)
208 : {
209 54 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
210 :
211 : jsval lowerVal, upperVal;
212 54 : JSBool lowerOpen = false, upperOpen = false;
213 54 : if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "vv/bb", &lowerVal,
214 54 : &upperVal, &lowerOpen, &upperOpen)) {
215 0 : return false;
216 : }
217 :
218 108 : nsRefPtr<IDBKeyRange> keyRange = new IDBKeyRange(lowerOpen, upperOpen, false);
219 :
220 108 : if (!GetKeyFromJSValOrThrow(aCx, lowerVal, keyRange->Lower()) ||
221 54 : !GetKeyFromJSValOrThrow(aCx, upperVal, keyRange->Upper())) {
222 0 : return false;
223 : }
224 :
225 107 : if (keyRange->Lower() > keyRange->Upper() ||
226 53 : (keyRange->Lower() == keyRange->Upper() && (lowerOpen || upperOpen))) {
227 3 : ThrowException(aCx, NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
228 3 : return false;
229 : }
230 :
231 51 : return ReturnKeyRange(aCx, aVp, keyRange);
232 : }
233 :
234 : #define KEYRANGE_FUNCTION_FLAGS (JSPROP_ENUMERATE | JSPROP_PERMANENT)
235 :
236 : const JSFunctionSpec gKeyRangeConstructors[] = {
237 : JS_FN("only", MakeOnlyKeyRange, 1, KEYRANGE_FUNCTION_FLAGS),
238 : JS_FN("lowerBound", MakeLowerBoundKeyRange, 1, KEYRANGE_FUNCTION_FLAGS),
239 : JS_FN("upperBound", MakeUpperBoundKeyRange, 1, KEYRANGE_FUNCTION_FLAGS),
240 : JS_FN("bound", MakeBoundKeyRange, 2, KEYRANGE_FUNCTION_FLAGS),
241 : JS_FS_END
242 : };
243 :
244 : #undef KEYRANGE_FUNCTION_FLAGS
245 :
246 : } // anonymous namespace
247 :
248 : // static
249 : JSBool
250 50 : IDBKeyRange::DefineConstructors(JSContext* aCx,
251 : JSObject* aObject)
252 : {
253 50 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
254 50 : NS_ASSERTION(aCx, "Null pointer!");
255 50 : NS_ASSERTION(aObject, "Null pointer!");
256 :
257 : // Add the constructor methods for key ranges.
258 : return JS_DefineFunctions(aCx, aObject,
259 50 : const_cast<JSFunctionSpec*>(gKeyRangeConstructors));
260 : }
261 :
262 : // static
263 : nsresult
264 413 : IDBKeyRange::FromJSVal(JSContext* aCx,
265 : const jsval& aVal,
266 : IDBKeyRange** aKeyRange)
267 : {
268 : nsresult rv;
269 826 : nsRefPtr<IDBKeyRange> keyRange;
270 :
271 413 : if (JSVAL_IS_VOID(aVal) || JSVAL_IS_NULL(aVal)) {
272 : // undefined and null returns no IDBKeyRange.
273 : }
274 670 : else if (JSVAL_IS_PRIMITIVE(aVal) ||
275 147 : JS_IsArrayObject(aCx, JSVAL_TO_OBJECT(aVal)) ||
276 147 : JS_ObjectIsDate(aCx, JSVAL_TO_OBJECT(aVal))) {
277 : // A valid key returns an 'only' IDBKeyRange.
278 229 : keyRange = new IDBKeyRange(false, false, true);
279 :
280 229 : rv = GetKeyFromJSVal(aCx, aVal, keyRange->Lower());
281 229 : if (NS_FAILED(rv)) {
282 0 : return rv;
283 : }
284 : }
285 : else {
286 : // An object is not permitted unless it's another IDBKeyRange.
287 147 : nsIXPConnect* xpc = nsContentUtils::XPConnect();
288 147 : NS_ASSERTION(xpc, "This should never be null!");
289 :
290 294 : nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
291 : rv = xpc->GetWrappedNativeOfJSObject(aCx, JSVAL_TO_OBJECT(aVal),
292 147 : getter_AddRefs(wrapper));
293 147 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
294 :
295 292 : nsCOMPtr<nsIIDBKeyRange> iface;
296 146 : if (!wrapper || !(iface = do_QueryInterface(wrapper->Native()))) {
297 : // Some random JS object?
298 0 : return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
299 : }
300 :
301 292 : keyRange = static_cast<IDBKeyRange*>(iface.get());
302 : }
303 :
304 412 : keyRange.forget(aKeyRange);
305 412 : return NS_OK;
306 : }
307 :
308 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(IDBKeyRange)
309 :
310 2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBKeyRange)
311 2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
312 2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
313 :
314 2 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBKeyRange)
315 2 : if (JSVAL_IS_GCTHING(tmp->mCachedLowerVal)) {
316 0 : void *gcThing = JSVAL_TO_GCTHING(tmp->mCachedLowerVal);
317 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(gcThing, "mCachedLowerVal")
318 : }
319 2 : if (JSVAL_IS_GCTHING(tmp->mCachedUpperVal)) {
320 0 : void *gcThing = JSVAL_TO_GCTHING(tmp->mCachedUpperVal);
321 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(gcThing, "mCachedUpperVal")
322 : }
323 2 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
324 :
325 2 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBKeyRange)
326 2 : if (tmp->mRooted) {
327 0 : NS_DROP_JS_OBJECTS(tmp, IDBKeyRange);
328 0 : tmp->mCachedLowerVal = JSVAL_VOID;
329 0 : tmp->mCachedUpperVal = JSVAL_VOID;
330 0 : tmp->mHaveCachedLowerVal = false;
331 0 : tmp->mHaveCachedUpperVal = false;
332 0 : tmp->mRooted = false;
333 : }
334 2 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
335 :
336 1789 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBKeyRange)
337 998 : NS_INTERFACE_MAP_ENTRY(nsISupports)
338 856 : NS_INTERFACE_MAP_ENTRY(nsIIDBKeyRange)
339 568 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBKeyRange)
340 426 : NS_INTERFACE_MAP_END
341 :
342 1327 : NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBKeyRange)
343 1701 : NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBKeyRange)
344 :
345 : DOMCI_DATA(IDBKeyRange, IDBKeyRange)
346 :
347 748 : IDBKeyRange::~IDBKeyRange()
348 : {
349 374 : if (mRooted) {
350 0 : NS_DROP_JS_OBJECTS(this, IDBKeyRange);
351 0 : mRooted = false;
352 : }
353 374 : }
354 :
355 : NS_IMETHODIMP
356 0 : IDBKeyRange::GetLower(JSContext* aCx,
357 : jsval* aLower)
358 : {
359 0 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
360 :
361 0 : if (!mHaveCachedLowerVal) {
362 0 : if (!mRooted) {
363 0 : NS_HOLD_JS_OBJECTS(this, IDBKeyRange);
364 0 : mRooted = true;
365 : }
366 :
367 0 : nsresult rv = Lower().ToJSVal(aCx, &mCachedLowerVal);
368 0 : NS_ENSURE_SUCCESS(rv, rv);
369 :
370 0 : mHaveCachedLowerVal = true;
371 : }
372 :
373 0 : *aLower = mCachedLowerVal;
374 0 : return NS_OK;
375 : }
376 :
377 : NS_IMETHODIMP
378 0 : IDBKeyRange::GetUpper(JSContext* aCx,
379 : jsval* aUpper)
380 : {
381 0 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
382 :
383 0 : if (!mHaveCachedUpperVal) {
384 0 : if (!mRooted) {
385 0 : NS_HOLD_JS_OBJECTS(this, IDBKeyRange);
386 0 : mRooted = true;
387 : }
388 :
389 0 : nsresult rv = Upper().ToJSVal(aCx, &mCachedUpperVal);
390 0 : NS_ENSURE_SUCCESS(rv, rv);
391 :
392 0 : mHaveCachedUpperVal = true;
393 : }
394 :
395 0 : *aUpper = mCachedUpperVal;
396 0 : return NS_OK;
397 : }
398 :
399 : NS_IMETHODIMP
400 0 : IDBKeyRange::GetLowerOpen(bool* aLowerOpen)
401 : {
402 0 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
403 :
404 0 : *aLowerOpen = mLowerOpen;
405 0 : return NS_OK;
406 : }
407 :
408 :
409 : NS_IMETHODIMP
410 0 : IDBKeyRange::GetUpperOpen(bool* aUpperOpen)
411 : {
412 0 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
413 :
414 0 : *aUpperOpen = mUpperOpen;
415 0 : return NS_OK;
416 4392 : }
|