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 "IDBCursor.h"
41 :
42 : #include "mozilla/storage.h"
43 : #include "nsComponentManagerUtils.h"
44 : #include "nsContentUtils.h"
45 : #include "nsDOMClassInfoID.h"
46 : #include "nsEventDispatcher.h"
47 : #include "nsJSUtils.h"
48 : #include "nsThreadUtils.h"
49 :
50 : #include "AsyncConnectionHelper.h"
51 : #include "IDBEvents.h"
52 : #include "IDBIndex.h"
53 : #include "IDBObjectStore.h"
54 : #include "IDBTransaction.h"
55 : #include "TransactionThreadPool.h"
56 :
57 : USING_INDEXEDDB_NAMESPACE
58 :
59 : namespace {
60 :
61 : inline
62 : already_AddRefed<IDBRequest>
63 : GenerateRequest(IDBCursor* aCursor)
64 : {
65 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
66 : IDBDatabase* database = aCursor->Transaction()->Database();
67 : return IDBRequest::Create(aCursor, database, aCursor->Transaction());
68 : }
69 :
70 : } // anonymous namespace
71 :
72 : BEGIN_INDEXEDDB_NAMESPACE
73 :
74 : class ContinueHelper : public AsyncConnectionHelper
75 : {
76 : public:
77 1778 : ContinueHelper(IDBCursor* aCursor,
78 : PRInt32 aCount)
79 : : AsyncConnectionHelper(aCursor->mTransaction, aCursor->mRequest),
80 1778 : mCursor(aCursor), mCount(aCount)
81 : {
82 1778 : NS_ASSERTION(aCount > 0, "Must have a count!");
83 1778 : }
84 :
85 1778 : ~ContinueHelper()
86 3556 : {
87 1778 : IDBObjectStore::ClearStructuredCloneBuffer(mCloneReadInfo.mCloneBuffer);
88 3556 : }
89 :
90 : nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
91 : nsresult GetSuccessResult(JSContext* aCx,
92 : jsval* aVal);
93 :
94 1778 : void ReleaseMainThreadObjects()
95 : {
96 1778 : mCursor = nsnull;
97 1778 : AsyncConnectionHelper::ReleaseMainThreadObjects();
98 1778 : }
99 :
100 : protected:
101 : virtual nsresult
102 : BindArgumentsToStatement(mozIStorageStatement* aStatement) = 0;
103 :
104 : virtual nsresult
105 : GatherResultsFromStatement(mozIStorageStatement* aStatement) = 0;
106 :
107 : protected:
108 : nsRefPtr<IDBCursor> mCursor;
109 : PRInt32 mCount;
110 : Key mKey;
111 : Key mObjectKey;
112 : StructuredCloneReadInfo mCloneReadInfo;
113 : };
114 :
115 : class ContinueObjectStoreHelper : public ContinueHelper
116 5292 : {
117 : public:
118 1323 : ContinueObjectStoreHelper(IDBCursor* aCursor,
119 : PRUint32 aCount)
120 1323 : : ContinueHelper(aCursor, aCount)
121 1323 : { }
122 :
123 : private:
124 : nsresult BindArgumentsToStatement(mozIStorageStatement* aStatement);
125 : nsresult GatherResultsFromStatement(mozIStorageStatement* aStatement);
126 : };
127 :
128 : class ContinueIndexHelper : public ContinueHelper
129 1472 : {
130 : public:
131 455 : ContinueIndexHelper(IDBCursor* aCursor,
132 : PRUint32 aCount)
133 455 : : ContinueHelper(aCursor, aCount)
134 455 : { }
135 :
136 : private:
137 : nsresult BindArgumentsToStatement(mozIStorageStatement* aStatement);
138 : nsresult GatherResultsFromStatement(mozIStorageStatement* aStatement);
139 : };
140 :
141 : class ContinueIndexObjectHelper : public ContinueIndexHelper
142 696 : {
143 : public:
144 174 : ContinueIndexObjectHelper(IDBCursor* aCursor,
145 : PRUint32 aCount)
146 174 : : ContinueIndexHelper(aCursor, aCount)
147 174 : { }
148 :
149 : private:
150 : nsresult GatherResultsFromStatement(mozIStorageStatement* aStatement);
151 : };
152 :
153 : END_INDEXEDDB_NAMESPACE
154 :
155 : // static
156 : already_AddRefed<IDBCursor>
157 38 : IDBCursor::Create(IDBRequest* aRequest,
158 : IDBTransaction* aTransaction,
159 : IDBObjectStore* aObjectStore,
160 : Direction aDirection,
161 : const Key& aRangeKey,
162 : const nsACString& aContinueQuery,
163 : const nsACString& aContinueToQuery,
164 : const Key& aKey,
165 : StructuredCloneReadInfo& aCloneReadInfo)
166 : {
167 38 : NS_ASSERTION(aObjectStore, "Null pointer!");
168 38 : NS_ASSERTION(!aKey.IsUnset(), "Bad key!");
169 :
170 : nsRefPtr<IDBCursor> cursor =
171 : IDBCursor::CreateCommon(aRequest, aTransaction, aObjectStore, aDirection,
172 76 : aRangeKey, aContinueQuery, aContinueToQuery);
173 38 : NS_ASSERTION(cursor, "This shouldn't fail!");
174 :
175 38 : cursor->mObjectStore = aObjectStore;
176 38 : cursor->mType = OBJECTSTORE;
177 38 : cursor->mKey = aKey;
178 38 : cursor->mCloneReadInfo.Swap(aCloneReadInfo);
179 :
180 38 : return cursor.forget();
181 : }
182 :
183 : // static
184 : already_AddRefed<IDBCursor>
185 74 : IDBCursor::Create(IDBRequest* aRequest,
186 : IDBTransaction* aTransaction,
187 : IDBIndex* aIndex,
188 : Direction aDirection,
189 : const Key& aRangeKey,
190 : const nsACString& aContinueQuery,
191 : const nsACString& aContinueToQuery,
192 : const Key& aKey,
193 : const Key& aObjectKey)
194 : {
195 74 : NS_ASSERTION(aIndex, "Null pointer!");
196 74 : NS_ASSERTION(!aKey.IsUnset(), "Bad key!");
197 74 : NS_ASSERTION(!aObjectKey.IsUnset(), "Bad key!");
198 :
199 : nsRefPtr<IDBCursor> cursor =
200 : IDBCursor::CreateCommon(aRequest, aTransaction, aIndex->ObjectStore(),
201 : aDirection, aRangeKey, aContinueQuery,
202 148 : aContinueToQuery);
203 74 : NS_ASSERTION(cursor, "This shouldn't fail!");
204 :
205 74 : cursor->mIndex = aIndex;
206 74 : cursor->mType = INDEXKEY;
207 74 : cursor->mKey = aKey,
208 74 : cursor->mObjectKey = aObjectKey;
209 :
210 74 : return cursor.forget();
211 : }
212 :
213 : // static
214 : already_AddRefed<IDBCursor>
215 51 : IDBCursor::Create(IDBRequest* aRequest,
216 : IDBTransaction* aTransaction,
217 : IDBIndex* aIndex,
218 : Direction aDirection,
219 : const Key& aRangeKey,
220 : const nsACString& aContinueQuery,
221 : const nsACString& aContinueToQuery,
222 : const Key& aKey,
223 : const Key& aObjectKey,
224 : StructuredCloneReadInfo& aCloneReadInfo)
225 : {
226 51 : NS_ASSERTION(aIndex, "Null pointer!");
227 51 : NS_ASSERTION(!aKey.IsUnset(), "Bad key!");
228 :
229 : nsRefPtr<IDBCursor> cursor =
230 : IDBCursor::CreateCommon(aRequest, aTransaction, aIndex->ObjectStore(),
231 : aDirection, aRangeKey, aContinueQuery,
232 102 : aContinueToQuery);
233 51 : NS_ASSERTION(cursor, "This shouldn't fail!");
234 :
235 51 : cursor->mObjectStore = aIndex->ObjectStore();
236 51 : cursor->mIndex = aIndex;
237 51 : cursor->mType = INDEXOBJECT;
238 51 : cursor->mKey = aKey;
239 51 : cursor->mObjectKey = aObjectKey;
240 51 : cursor->mCloneReadInfo.Swap(aCloneReadInfo);
241 :
242 51 : return cursor.forget();
243 : }
244 :
245 : // static
246 : nsresult
247 16 : IDBCursor::ParseDirection(const nsAString& aDirection, Direction* aResult)
248 : {
249 16 : if (aDirection.EqualsLiteral("next")) {
250 4 : *aResult = NEXT;
251 : }
252 12 : else if (aDirection.EqualsLiteral("nextunique")) {
253 2 : *aResult = NEXT_UNIQUE;
254 : }
255 10 : else if (aDirection.EqualsLiteral("prev")) {
256 8 : *aResult = PREV;
257 : }
258 2 : else if (aDirection.EqualsLiteral("prevunique")) {
259 2 : *aResult = PREV_UNIQUE;
260 : }
261 : else {
262 0 : return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
263 : }
264 :
265 16 : return NS_OK;
266 : }
267 :
268 : // static
269 : already_AddRefed<IDBCursor>
270 163 : IDBCursor::CreateCommon(IDBRequest* aRequest,
271 : IDBTransaction* aTransaction,
272 : IDBObjectStore* aObjectStore,
273 : Direction aDirection,
274 : const Key& aRangeKey,
275 : const nsACString& aContinueQuery,
276 : const nsACString& aContinueToQuery)
277 : {
278 163 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
279 163 : NS_ASSERTION(aRequest, "Null pointer!");
280 163 : NS_ASSERTION(aTransaction, "Null pointer!");
281 163 : NS_ASSERTION(aObjectStore, "Null pointer!");
282 163 : NS_ASSERTION(!aContinueQuery.IsEmpty(), "Empty query!");
283 163 : NS_ASSERTION(!aContinueToQuery.IsEmpty(), "Empty query!");
284 :
285 326 : nsRefPtr<IDBCursor> cursor = new IDBCursor();
286 :
287 163 : IDBDatabase* database = aTransaction->Database();
288 163 : cursor->mScriptOwner = database->GetScriptOwner();
289 :
290 163 : if (cursor->mScriptOwner) {
291 163 : if (NS_FAILED(NS_HOLD_JS_OBJECTS(cursor, IDBCursor))) {
292 0 : return nsnull;
293 : }
294 :
295 163 : cursor->mRooted = true;
296 : }
297 :
298 163 : cursor->mRequest = aRequest;
299 163 : cursor->mTransaction = aTransaction;
300 163 : cursor->mObjectStore = aObjectStore;
301 163 : cursor->mDirection = aDirection;
302 163 : cursor->mContinueQuery = aContinueQuery;
303 163 : cursor->mContinueToQuery = aContinueToQuery;
304 163 : cursor->mRangeKey = aRangeKey;
305 :
306 163 : return cursor.forget();
307 : }
308 :
309 163 : IDBCursor::IDBCursor()
310 : : mScriptOwner(nsnull),
311 : mType(OBJECTSTORE),
312 : mDirection(IDBCursor::NEXT),
313 : mCachedKey(JSVAL_VOID),
314 : mCachedPrimaryKey(JSVAL_VOID),
315 : mCachedValue(JSVAL_VOID),
316 : mHaveCachedKey(false),
317 : mHaveCachedPrimaryKey(false),
318 : mHaveCachedValue(false),
319 : mRooted(false),
320 : mContinueCalled(false),
321 163 : mHaveValue(true)
322 : {
323 163 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
324 163 : }
325 :
326 326 : IDBCursor::~IDBCursor()
327 : {
328 163 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
329 :
330 163 : if (mRooted) {
331 155 : NS_DROP_JS_OBJECTS(this, IDBCursor);
332 : }
333 163 : IDBObjectStore::ClearStructuredCloneBuffer(mCloneReadInfo.mCloneBuffer);
334 163 : }
335 :
336 : nsresult
337 1788 : IDBCursor::ContinueInternal(const Key& aKey,
338 : PRInt32 aCount)
339 : {
340 1788 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
341 1788 : NS_ASSERTION(aCount > 0, "Must have a count!");
342 :
343 1788 : if (!mTransaction->IsOpen()) {
344 0 : return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
345 : }
346 :
347 1788 : if (!mHaveValue || mContinueCalled) {
348 10 : return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
349 : }
350 :
351 1778 : mContinueToKey = aKey;
352 :
353 : #ifdef DEBUG
354 : {
355 3556 : nsAutoString readyState;
356 1778 : if (NS_FAILED(mRequest->GetReadyState(readyState))) {
357 0 : NS_ERROR("This should never fail!");
358 : }
359 1778 : NS_ASSERTION(readyState.EqualsLiteral("done"), "Should be DONE!");
360 : }
361 : #endif
362 :
363 1778 : mRequest->Reset();
364 :
365 3556 : nsRefPtr<ContinueHelper> helper;
366 1778 : switch (mType) {
367 : case OBJECTSTORE:
368 1323 : helper = new ContinueObjectStoreHelper(this, aCount);
369 1323 : break;
370 :
371 : case INDEXKEY:
372 281 : helper = new ContinueIndexHelper(this, aCount);
373 281 : break;
374 :
375 : case INDEXOBJECT:
376 174 : helper = new ContinueIndexObjectHelper(this, aCount);
377 174 : break;
378 :
379 : default:
380 0 : NS_NOTREACHED("Unknown cursor type!");
381 : }
382 :
383 1778 : nsresult rv = helper->DispatchToTransactionPool();
384 1778 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
385 :
386 1778 : mContinueCalled = true;
387 1778 : return NS_OK;
388 : }
389 :
390 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(IDBCursor)
391 :
392 8 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBCursor)
393 8 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
394 8 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mRequest,
395 : nsIDOMEventTarget)
396 8 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mTransaction,
397 : nsIDOMEventTarget)
398 8 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mObjectStore)
399 8 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mIndex)
400 8 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
401 :
402 221 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBCursor)
403 221 : NS_ASSERTION(tmp->mHaveCachedKey || JSVAL_IS_VOID(tmp->mCachedKey),
404 : "Should have a cached key");
405 221 : NS_ASSERTION(tmp->mHaveCachedPrimaryKey ||
406 : JSVAL_IS_VOID(tmp->mCachedPrimaryKey),
407 : "Should have a cached primary key");
408 221 : NS_ASSERTION(tmp->mHaveCachedValue || JSVAL_IS_VOID(tmp->mCachedValue),
409 : "Should have a cached value");
410 221 : if (tmp->mScriptOwner) {
411 221 : NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(tmp->mScriptOwner,
412 : "mScriptOwner")
413 : }
414 221 : if (JSVAL_IS_GCTHING(tmp->mCachedKey)) {
415 0 : void *gcThing = JSVAL_TO_GCTHING(tmp->mCachedKey);
416 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(gcThing, "mCachedKey")
417 : }
418 221 : if (JSVAL_IS_GCTHING(tmp->mCachedPrimaryKey)) {
419 0 : void *gcThing = JSVAL_TO_GCTHING(tmp->mCachedPrimaryKey);
420 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(gcThing, "mCachedPrimaryKey")
421 : }
422 221 : if (JSVAL_IS_GCTHING(tmp->mCachedValue)) {
423 48 : void *gcThing = JSVAL_TO_GCTHING(tmp->mCachedValue);
424 48 : NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(gcThing, "mCachedValue")
425 : }
426 221 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
427 :
428 8 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBCursor)
429 : // Don't unlink mObjectStore, mIndex, or mTransaction!
430 8 : if (tmp->mRooted) {
431 8 : NS_DROP_JS_OBJECTS(tmp, IDBCursor);
432 8 : tmp->mScriptOwner = nsnull;
433 8 : tmp->mCachedKey = JSVAL_VOID;
434 8 : tmp->mCachedPrimaryKey = JSVAL_VOID;
435 8 : tmp->mCachedValue = JSVAL_VOID;
436 8 : tmp->mHaveCachedKey = false;
437 8 : tmp->mHaveCachedPrimaryKey = false;
438 8 : tmp->mHaveCachedValue = false;
439 8 : tmp->mRooted = false;
440 8 : tmp->mHaveValue = false;
441 : }
442 8 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRequest)
443 8 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
444 :
445 17084 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBCursor)
446 11184 : NS_INTERFACE_MAP_ENTRY(nsIIDBCursor)
447 8238 : NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIDBCursorWithValue, mType != INDEXKEY)
448 7541 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(IDBCursorWithValue,
449 : mType != INDEXKEY)
450 7452 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(IDBCursor,
451 : mType == INDEXKEY)
452 7378 : NS_INTERFACE_MAP_ENTRY(nsISupports)
453 5592 : NS_INTERFACE_MAP_END
454 :
455 7378 : NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBCursor)
456 7541 : NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBCursor)
457 :
458 : DOMCI_DATA(IDBCursor, IDBCursor)
459 : DOMCI_DATA(IDBCursorWithValue, IDBCursor)
460 :
461 : NS_IMETHODIMP
462 0 : IDBCursor::GetDirection(nsAString& aDirection)
463 : {
464 0 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
465 :
466 0 : switch(mDirection) {
467 : case NEXT:
468 0 : aDirection.AssignLiteral("next");
469 0 : break;
470 : case NEXT_UNIQUE:
471 0 : aDirection.AssignLiteral("nextunique");
472 0 : break;
473 : case PREV:
474 0 : aDirection.AssignLiteral("prev");
475 0 : break;
476 : case PREV_UNIQUE:
477 0 : aDirection.AssignLiteral("prevunique");
478 : }
479 :
480 0 : return NS_OK;
481 : }
482 :
483 : NS_IMETHODIMP
484 0 : IDBCursor::GetSource(nsISupports** aSource)
485 : {
486 0 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
487 :
488 : return mType == OBJECTSTORE ?
489 0 : CallQueryInterface(mObjectStore, aSource) :
490 0 : CallQueryInterface(mIndex, aSource);
491 : }
492 :
493 : NS_IMETHODIMP
494 659 : IDBCursor::GetKey(JSContext* aCx,
495 : jsval* aKey)
496 : {
497 659 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
498 :
499 659 : NS_ASSERTION(!mKey.IsUnset() || !mHaveValue, "Bad key!");
500 :
501 659 : if (!mHaveValue) {
502 0 : *aKey = JSVAL_VOID;
503 0 : return NS_OK;
504 : }
505 :
506 659 : if (!mHaveCachedKey) {
507 429 : if (!mRooted) {
508 0 : NS_HOLD_JS_OBJECTS(this, IDBCursor);
509 0 : mRooted = true;
510 : }
511 :
512 858 : JSAutoRequest ar(aCx);
513 :
514 429 : nsresult rv = mKey.ToJSVal(aCx, &mCachedKey);
515 429 : NS_ENSURE_SUCCESS(rv, rv);
516 :
517 858 : mHaveCachedKey = true;
518 : }
519 :
520 659 : *aKey = mCachedKey;
521 659 : return NS_OK;
522 : }
523 :
524 : NS_IMETHODIMP
525 469 : IDBCursor::GetPrimaryKey(JSContext* aCx,
526 : jsval* aValue)
527 : {
528 469 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
529 :
530 469 : if (!mHaveValue) {
531 0 : *aValue = JSVAL_VOID;
532 0 : return NS_OK;
533 : }
534 :
535 469 : if (!mHaveCachedPrimaryKey) {
536 365 : if (!mRooted) {
537 0 : NS_HOLD_JS_OBJECTS(this, IDBCursor);
538 0 : mRooted = true;
539 : }
540 :
541 730 : JSAutoRequest ar(aCx);
542 :
543 365 : NS_ASSERTION(mType == OBJECTSTORE ? !mKey.IsUnset() :
544 : !mObjectKey.IsUnset(), "Bad key!");
545 :
546 365 : const Key& key = mType == OBJECTSTORE ? mKey : mObjectKey;
547 :
548 365 : nsresult rv = key.ToJSVal(aCx, &mCachedPrimaryKey);
549 365 : NS_ENSURE_SUCCESS(rv, rv);
550 :
551 730 : mHaveCachedPrimaryKey = true;
552 : }
553 :
554 469 : *aValue = mCachedPrimaryKey;
555 469 : return NS_OK;
556 : }
557 :
558 : NS_IMETHODIMP
559 697 : IDBCursor::GetValue(JSContext* aCx,
560 : jsval* aValue)
561 : {
562 697 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
563 697 : NS_ASSERTION(mType != INDEXKEY, "GetValue shouldn't exist on index keys");
564 :
565 697 : if (!mHaveValue) {
566 0 : *aValue = JSVAL_VOID;
567 0 : return NS_OK;
568 : }
569 :
570 697 : if (!mHaveCachedValue) {
571 288 : if (!mRooted) {
572 0 : NS_HOLD_JS_OBJECTS(this, IDBCursor);
573 0 : mRooted = true;
574 : }
575 :
576 288 : if (!IDBObjectStore::DeserializeValue(aCx, mCloneReadInfo, &mCachedValue)) {
577 0 : mCachedValue = JSVAL_VOID;
578 0 : return NS_ERROR_DOM_DATA_CLONE_ERR;
579 : }
580 :
581 288 : mCloneReadInfo.mCloneBuffer.clear();
582 288 : mHaveCachedValue = true;
583 : }
584 :
585 697 : *aValue = mCachedValue;
586 697 : return NS_OK;
587 : }
588 :
589 : NS_IMETHODIMP
590 1782 : IDBCursor::Continue(const jsval &aKey,
591 : JSContext* aCx)
592 : {
593 1782 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
594 :
595 3564 : Key key;
596 1782 : nsresult rv = key.SetFromJSVal(aCx, aKey);
597 1782 : NS_ENSURE_SUCCESS(rv, rv);
598 :
599 1782 : if (!key.IsUnset()) {
600 7 : switch (mDirection) {
601 : case IDBCursor::NEXT:
602 : case IDBCursor::NEXT_UNIQUE:
603 7 : if (key <= mKey) {
604 0 : return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
605 : }
606 7 : break;
607 :
608 : case IDBCursor::PREV:
609 : case IDBCursor::PREV_UNIQUE:
610 0 : if (key >= mKey) {
611 0 : return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
612 : }
613 0 : break;
614 :
615 : default:
616 0 : NS_NOTREACHED("Unknown direction type!");
617 : }
618 : }
619 :
620 1782 : return ContinueInternal(key, 1);
621 : }
622 :
623 : NS_IMETHODIMP
624 21 : IDBCursor::Update(const jsval& aValue,
625 : JSContext* aCx,
626 : nsIIDBRequest** _retval)
627 : {
628 21 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
629 :
630 21 : if (!mTransaction->IsOpen()) {
631 0 : return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
632 : }
633 :
634 21 : if (!mTransaction->IsWriteAllowed()) {
635 0 : return NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR;
636 : }
637 :
638 21 : if (!mHaveValue || mType == INDEXKEY) {
639 0 : return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
640 : }
641 :
642 21 : NS_ASSERTION(mObjectStore, "This cannot be null!");
643 21 : NS_ASSERTION(!mKey.IsUnset() , "Bad key!");
644 21 : NS_ASSERTION(mType != INDEXOBJECT || !mObjectKey.IsUnset(), "Bad key!");
645 :
646 : nsresult rv;
647 :
648 42 : JSAutoRequest ar(aCx);
649 :
650 21 : Key& objectKey = (mType == OBJECTSTORE) ? mKey : mObjectKey;
651 :
652 21 : if (!mObjectStore->KeyPath().IsEmpty()) {
653 : // This has to be an object.
654 14 : if (JSVAL_IS_PRIMITIVE(aValue)) {
655 0 : return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
656 : }
657 :
658 : // Make sure the object given has the correct keyPath value set on it.
659 14 : const nsString& keyPath = mObjectStore->KeyPath();
660 :
661 : jsval prop;
662 : JSBool ok = JS_GetUCProperty(aCx, JSVAL_TO_OBJECT(aValue),
663 14 : reinterpret_cast<const jschar*>(keyPath.get()),
664 28 : keyPath.Length(), &prop);
665 14 : NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
666 :
667 28 : Key key;
668 14 : rv = key.SetFromJSVal(aCx, prop);
669 14 : if (NS_FAILED(rv)) {
670 0 : return rv;
671 : }
672 :
673 14 : if (key != objectKey) {
674 0 : return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
675 : }
676 :
677 14 : return mObjectStore->Put(aValue, JSVAL_VOID, aCx, 0, _retval);
678 : }
679 :
680 : jsval keyVal;
681 7 : rv = objectKey.ToJSVal(aCx, &keyVal);
682 7 : NS_ENSURE_SUCCESS(rv, rv);
683 :
684 7 : return mObjectStore->Put(aValue, keyVal, aCx, 1, _retval);
685 : }
686 :
687 : NS_IMETHODIMP
688 9 : IDBCursor::Delete(JSContext* aCx,
689 : nsIIDBRequest** _retval)
690 : {
691 9 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
692 :
693 9 : if (!mTransaction->IsOpen()) {
694 0 : return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
695 : }
696 :
697 9 : if (!mTransaction->IsWriteAllowed()) {
698 0 : return NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR;
699 : }
700 :
701 9 : if (!mHaveValue || mType == INDEXKEY) {
702 0 : return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
703 : }
704 :
705 9 : NS_ASSERTION(mObjectStore, "This cannot be null!");
706 9 : NS_ASSERTION(!mKey.IsUnset() , "Bad key!");
707 :
708 9 : Key& objectKey = (mType == OBJECTSTORE) ? mKey : mObjectKey;
709 :
710 : jsval key;
711 9 : nsresult rv = objectKey.ToJSVal(aCx, &key);
712 9 : NS_ENSURE_SUCCESS(rv, rv);
713 :
714 9 : return mObjectStore->Delete(key, aCx, _retval);
715 : }
716 :
717 : NS_IMETHODIMP
718 6 : IDBCursor::Advance(PRInt32 aCount)
719 : {
720 6 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
721 :
722 6 : if (aCount < 1) {
723 0 : return NS_ERROR_DOM_TYPE_ERR;
724 : }
725 :
726 12 : Key key;
727 6 : return ContinueInternal(key, aCount);
728 : }
729 :
730 : nsresult
731 1778 : ContinueHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
732 : {
733 : // We need to pick a query based on whether or not the cursor's mContinueToKey
734 : // is set. If it is unset then othing was passed to continue so we'll grab the
735 : // next item in the database that is greater than (less than, if we're running
736 : // a PREV cursor) the current key. If it is set then a key was passed to
737 : // continue so we'll grab the next item in the database that is greater than
738 : // (less than, if we're running a PREV cursor) or equal to the key that was
739 : // specified.
740 :
741 3556 : nsCAutoString query;
742 1778 : if (mCursor->mContinueToKey.IsUnset()) {
743 1771 : query.Assign(mCursor->mContinueQuery);
744 : }
745 : else {
746 7 : query.Assign(mCursor->mContinueToQuery);
747 : }
748 1778 : NS_ASSERTION(!query.IsEmpty(), "Bad query!");
749 :
750 1778 : query.AppendInt(mCount);
751 :
752 3556 : nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
753 1778 : NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
754 :
755 3556 : mozStorageStatementScoper scoper(stmt);
756 :
757 1778 : nsresult rv = BindArgumentsToStatement(stmt);
758 1778 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
759 :
760 1778 : NS_ASSERTION(mCount > 0, "Not ok!");
761 :
762 : bool hasResult;
763 3495 : for (PRInt32 index = 0; index < mCount; index++) {
764 1872 : rv = stmt->ExecuteStep(&hasResult);
765 1872 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
766 :
767 1872 : if (!hasResult) {
768 155 : break;
769 : }
770 : }
771 :
772 1778 : if (hasResult) {
773 1623 : rv = GatherResultsFromStatement(stmt);
774 1623 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
775 : }
776 : else {
777 155 : mKey.Unset();
778 : }
779 :
780 1778 : return NS_OK;
781 : }
782 :
783 : nsresult
784 1778 : ContinueHelper::GetSuccessResult(JSContext* aCx,
785 : jsval* aVal)
786 : {
787 : // Remove cached stuff from last time.
788 1778 : mCursor->mCachedKey = JSVAL_VOID;
789 1778 : mCursor->mCachedPrimaryKey = JSVAL_VOID;
790 1778 : mCursor->mCachedValue = JSVAL_VOID;
791 1778 : mCursor->mHaveCachedKey = false;
792 1778 : mCursor->mHaveCachedPrimaryKey = false;
793 1778 : mCursor->mHaveCachedValue = false;
794 1778 : mCursor->mContinueCalled = false;
795 :
796 1778 : if (mKey.IsUnset()) {
797 155 : mCursor->mHaveValue = false;
798 155 : *aVal = JSVAL_VOID;
799 : }
800 : else {
801 1623 : NS_ASSERTION(mCursor->mType == IDBCursor::OBJECTSTORE ||
802 : !mObjectKey.IsUnset(), "Bad key!");
803 :
804 : // Set new values.
805 1623 : mCursor->mKey = mKey;
806 1623 : mCursor->mObjectKey = mObjectKey;
807 1623 : mCursor->mContinueToKey.Unset();
808 :
809 1623 : mCursor->mCloneReadInfo.Swap(mCloneReadInfo);
810 1623 : mCloneReadInfo.mCloneBuffer.clear();
811 :
812 1623 : nsresult rv = WrapNative(aCx, mCursor, aVal);
813 1623 : NS_ENSURE_SUCCESS(rv, rv);
814 : }
815 :
816 1778 : return NS_OK;
817 : }
818 :
819 : nsresult
820 1323 : ContinueObjectStoreHelper::BindArgumentsToStatement(
821 : mozIStorageStatement* aStatement)
822 : {
823 : // Bind object store id.
824 1323 : nsresult rv = aStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"),
825 1323 : mCursor->mObjectStore->Id());
826 1323 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
827 :
828 2646 : NS_NAMED_LITERAL_CSTRING(currentKeyName, "current_key");
829 2646 : NS_NAMED_LITERAL_CSTRING(rangeKeyName, "range_key");
830 :
831 : // Bind current key.
832 1323 : const Key& currentKey = mCursor->mContinueToKey.IsUnset() ?
833 1320 : mCursor->mKey :
834 2643 : mCursor->mContinueToKey;
835 :
836 1323 : rv = currentKey.BindToStatement(aStatement, currentKeyName);
837 1323 : NS_ENSURE_SUCCESS(rv, rv);
838 :
839 : // Bind range key if it is specified.
840 1323 : const Key& rangeKey = mCursor->mRangeKey;
841 :
842 1323 : if (!rangeKey.IsUnset()) {
843 9 : rv = rangeKey.BindToStatement(aStatement, rangeKeyName);
844 9 : NS_ENSURE_SUCCESS(rv, rv);
845 : }
846 :
847 1323 : return NS_OK;
848 : }
849 :
850 : nsresult
851 1289 : ContinueObjectStoreHelper::GatherResultsFromStatement(
852 : mozIStorageStatement* aStatement)
853 : {
854 : // Figure out what kind of key we have next.
855 1289 : nsresult rv = mKey.SetFromStatement(aStatement, 0);
856 1289 : NS_ENSURE_SUCCESS(rv, rv);
857 :
858 : rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(aStatement, 1, 2,
859 1289 : mDatabase->Manager(), mCloneReadInfo);
860 1289 : NS_ENSURE_SUCCESS(rv, rv);
861 :
862 1289 : return NS_OK;
863 : }
864 :
865 : nsresult
866 455 : ContinueIndexHelper::BindArgumentsToStatement(mozIStorageStatement* aStatement)
867 : {
868 : // Bind index id.
869 455 : nsresult rv = aStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"),
870 455 : mCursor->mIndex->Id());
871 455 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
872 :
873 910 : NS_NAMED_LITERAL_CSTRING(currentKeyName, "current_key");
874 :
875 : // Bind current key.
876 455 : const Key& currentKey = mCursor->mContinueToKey.IsUnset() ?
877 451 : mCursor->mKey :
878 906 : mCursor->mContinueToKey;
879 :
880 455 : rv = currentKey.BindToStatement(aStatement, currentKeyName);
881 455 : NS_ENSURE_SUCCESS(rv, rv);
882 :
883 : // Bind range key if it is specified.
884 455 : if (!mCursor->mRangeKey.IsUnset()) {
885 276 : NS_NAMED_LITERAL_CSTRING(rangeKeyName, "range_key");
886 138 : rv = mCursor->mRangeKey.BindToStatement(aStatement, rangeKeyName);
887 138 : NS_ENSURE_SUCCESS(rv, rv);
888 : }
889 :
890 : // Bind object key if duplicates are allowed and we're not continuing to a
891 : // specific key.
892 938 : if ((mCursor->mDirection == IDBCursor::NEXT ||
893 40 : mCursor->mDirection == IDBCursor::PREV) &&
894 443 : mCursor->mContinueToKey.IsUnset()) {
895 439 : NS_ASSERTION(!mCursor->mObjectKey.IsUnset(), "Bad key!");
896 :
897 878 : NS_NAMED_LITERAL_CSTRING(objectKeyName, "object_key");
898 439 : rv = mCursor->mObjectKey.BindToStatement(aStatement, objectKeyName);
899 439 : NS_ENSURE_SUCCESS(rv, rv);
900 : }
901 :
902 455 : return NS_OK;
903 : }
904 :
905 : nsresult
906 207 : ContinueIndexHelper::GatherResultsFromStatement(
907 : mozIStorageStatement* aStatement)
908 : {
909 207 : nsresult rv = mKey.SetFromStatement(aStatement, 0);
910 207 : NS_ENSURE_SUCCESS(rv, rv);
911 :
912 207 : rv = mObjectKey.SetFromStatement(aStatement, 1);
913 207 : NS_ENSURE_SUCCESS(rv, rv);
914 :
915 207 : return NS_OK;
916 : }
917 :
918 : nsresult
919 127 : ContinueIndexObjectHelper::GatherResultsFromStatement(
920 : mozIStorageStatement* aStatement)
921 : {
922 127 : nsresult rv = mKey.SetFromStatement(aStatement, 0);
923 127 : NS_ENSURE_SUCCESS(rv, rv);
924 :
925 127 : rv = mObjectKey.SetFromStatement(aStatement, 1);
926 127 : NS_ENSURE_SUCCESS(rv, rv);
927 :
928 : rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(aStatement, 2, 3,
929 127 : mDatabase->Manager(), mCloneReadInfo);
930 127 : NS_ENSURE_SUCCESS(rv, rv);
931 :
932 127 : return NS_OK;
933 4392 : }
|