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 "IDBObjectStore.h"
41 :
42 : #include "nsIJSContextStack.h"
43 :
44 : #include "jsfriendapi.h"
45 : #include "mozilla/dom/StructuredCloneTags.h"
46 : #include "mozilla/storage.h"
47 : #include "nsCharSeparatedTokenizer.h"
48 : #include "nsContentUtils.h"
49 : #include "nsDOMClassInfo.h"
50 : #include "nsDOMFile.h"
51 : #include "nsDOMLists.h"
52 : #include "nsEventDispatcher.h"
53 : #include "nsJSUtils.h"
54 : #include "nsServiceManagerUtils.h"
55 : #include "nsThreadUtils.h"
56 : #include "snappy/snappy.h"
57 : #include "test_quota.h"
58 : #include "xpcpublic.h"
59 :
60 : #include "AsyncConnectionHelper.h"
61 : #include "IDBCursor.h"
62 : #include "IDBEvents.h"
63 : #include "IDBIndex.h"
64 : #include "IDBKeyRange.h"
65 : #include "IDBTransaction.h"
66 : #include "DatabaseInfo.h"
67 : #include "DictionaryHelpers.h"
68 :
69 : #define FILE_COPY_BUFFER_SIZE 32768
70 :
71 : USING_INDEXEDDB_NAMESPACE
72 :
73 : namespace {
74 :
75 : class AddHelper : public AsyncConnectionHelper
76 : {
77 : public:
78 1837 : AddHelper(IDBTransaction* aTransaction,
79 : IDBRequest* aRequest,
80 : IDBObjectStore* aObjectStore,
81 : StructuredCloneWriteInfo& aCloneWriteInfo,
82 : const Key& aKey,
83 : bool aOverwrite,
84 : nsTArray<IndexUpdateInfo>& aIndexUpdateInfo)
85 : : AsyncConnectionHelper(aTransaction, aRequest), mObjectStore(aObjectStore),
86 1837 : mKey(aKey), mOverwrite(aOverwrite)
87 : {
88 1837 : mCloneWriteInfo.Swap(aCloneWriteInfo);
89 1837 : mIndexUpdateInfo.SwapElements(aIndexUpdateInfo);
90 1837 : }
91 :
92 3674 : ~AddHelper()
93 3674 : {
94 1837 : IDBObjectStore::ClearStructuredCloneBuffer(mCloneWriteInfo.mCloneBuffer);
95 7348 : }
96 :
97 : nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
98 : nsresult GetSuccessResult(JSContext* aCx,
99 : jsval* aVal);
100 :
101 1837 : void ReleaseMainThreadObjects()
102 : {
103 1837 : mObjectStore = nsnull;
104 1837 : IDBObjectStore::ClearStructuredCloneBuffer(mCloneWriteInfo.mCloneBuffer);
105 1837 : AsyncConnectionHelper::ReleaseMainThreadObjects();
106 1837 : }
107 :
108 : private:
109 : // In-params.
110 : nsRefPtr<IDBObjectStore> mObjectStore;
111 :
112 : // These may change in the autoincrement case.
113 : StructuredCloneWriteInfo mCloneWriteInfo;
114 : Key mKey;
115 : const bool mOverwrite;
116 : nsTArray<IndexUpdateInfo> mIndexUpdateInfo;
117 : };
118 :
119 : class GetHelper : public AsyncConnectionHelper
120 : {
121 : public:
122 199 : GetHelper(IDBTransaction* aTransaction,
123 : IDBRequest* aRequest,
124 : IDBObjectStore* aObjectStore,
125 : IDBKeyRange* aKeyRange)
126 : : AsyncConnectionHelper(aTransaction, aRequest), mObjectStore(aObjectStore),
127 199 : mKeyRange(aKeyRange)
128 199 : { }
129 :
130 372 : ~GetHelper()
131 398 : {
132 199 : IDBObjectStore::ClearStructuredCloneBuffer(mCloneReadInfo.mCloneBuffer);
133 744 : }
134 :
135 : nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
136 : nsresult GetSuccessResult(JSContext* aCx,
137 : jsval* aVal);
138 :
139 199 : void ReleaseMainThreadObjects()
140 : {
141 199 : mObjectStore = nsnull;
142 199 : mKeyRange = nsnull;
143 199 : IDBObjectStore::ClearStructuredCloneBuffer(mCloneReadInfo.mCloneBuffer);
144 199 : AsyncConnectionHelper::ReleaseMainThreadObjects();
145 199 : }
146 :
147 : protected:
148 : // In-params.
149 : nsRefPtr<IDBObjectStore> mObjectStore;
150 : nsRefPtr<IDBKeyRange> mKeyRange;
151 :
152 : private:
153 : // Out-params.
154 : StructuredCloneReadInfo mCloneReadInfo;
155 : };
156 :
157 : class DeleteHelper : public GetHelper
158 104 : {
159 : public:
160 26 : DeleteHelper(IDBTransaction* aTransaction,
161 : IDBRequest* aRequest,
162 : IDBObjectStore* aObjectStore,
163 : IDBKeyRange* aKeyRange)
164 26 : : GetHelper(aTransaction, aRequest, aObjectStore, aKeyRange)
165 26 : { }
166 :
167 : nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
168 : nsresult GetSuccessResult(JSContext* aCx,
169 : jsval* aVal);
170 : };
171 :
172 : class ClearHelper : public AsyncConnectionHelper
173 40 : {
174 : public:
175 10 : ClearHelper(IDBTransaction* aTransaction,
176 : IDBRequest* aRequest,
177 : IDBObjectStore* aObjectStore)
178 10 : : AsyncConnectionHelper(aTransaction, aRequest), mObjectStore(aObjectStore)
179 10 : { }
180 :
181 : nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
182 :
183 10 : void ReleaseMainThreadObjects()
184 : {
185 10 : mObjectStore = nsnull;
186 10 : AsyncConnectionHelper::ReleaseMainThreadObjects();
187 10 : }
188 :
189 : protected:
190 : // In-params.
191 : nsRefPtr<IDBObjectStore> mObjectStore;
192 : };
193 :
194 : class OpenCursorHelper : public AsyncConnectionHelper
195 : {
196 : public:
197 47 : OpenCursorHelper(IDBTransaction* aTransaction,
198 : IDBRequest* aRequest,
199 : IDBObjectStore* aObjectStore,
200 : IDBKeyRange* aKeyRange,
201 : IDBCursor::Direction aDirection)
202 : : AsyncConnectionHelper(aTransaction, aRequest), mObjectStore(aObjectStore),
203 47 : mKeyRange(aKeyRange), mDirection(aDirection)
204 47 : { }
205 :
206 94 : ~OpenCursorHelper()
207 94 : {
208 47 : IDBObjectStore::ClearStructuredCloneBuffer(mCloneReadInfo.mCloneBuffer);
209 188 : }
210 :
211 : nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
212 : nsresult GetSuccessResult(JSContext* aCx,
213 : jsval* aVal);
214 :
215 47 : void ReleaseMainThreadObjects()
216 : {
217 47 : mObjectStore = nsnull;
218 47 : mKeyRange = nsnull;
219 47 : IDBObjectStore::ClearStructuredCloneBuffer(mCloneReadInfo.mCloneBuffer);
220 47 : AsyncConnectionHelper::ReleaseMainThreadObjects();
221 47 : }
222 :
223 : private:
224 : // In-params.
225 : nsRefPtr<IDBObjectStore> mObjectStore;
226 : nsRefPtr<IDBKeyRange> mKeyRange;
227 : const IDBCursor::Direction mDirection;
228 :
229 : // Out-params.
230 : Key mKey;
231 : StructuredCloneReadInfo mCloneReadInfo;
232 : nsCString mContinueQuery;
233 : nsCString mContinueToQuery;
234 : Key mRangeKey;
235 : };
236 :
237 : class CreateIndexHelper : public AsyncConnectionHelper
238 340 : {
239 : public:
240 : CreateIndexHelper(IDBTransaction* aTransaction, IDBIndex* aIndex);
241 :
242 : nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
243 :
244 84 : nsresult OnSuccess()
245 : {
246 84 : return NS_OK;
247 : }
248 :
249 1 : void OnError()
250 : {
251 1 : NS_ASSERTION(mTransaction->IsAborted(), "How else can this fail?!");
252 1 : }
253 :
254 85 : void ReleaseMainThreadObjects()
255 : {
256 85 : mIndex = nsnull;
257 85 : AsyncConnectionHelper::ReleaseMainThreadObjects();
258 85 : }
259 :
260 : private:
261 : nsresult InsertDataFromObjectStore(mozIStorageConnection* aConnection);
262 :
263 : static void DestroyTLSEntry(void* aPtr);
264 :
265 : static PRUintn sTLSIndex;
266 :
267 : // In-params.
268 : nsRefPtr<IDBIndex> mIndex;
269 : };
270 :
271 : PRUintn CreateIndexHelper::sTLSIndex = PRUintn(BAD_TLS_INDEX);
272 :
273 : class DeleteIndexHelper : public AsyncConnectionHelper
274 96 : {
275 : public:
276 24 : DeleteIndexHelper(IDBTransaction* aTransaction,
277 : const nsAString& aName,
278 : IDBObjectStore* aObjectStore)
279 : : AsyncConnectionHelper(aTransaction, nsnull), mName(aName),
280 24 : mObjectStore(aObjectStore)
281 24 : { }
282 :
283 : nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
284 :
285 24 : nsresult OnSuccess()
286 : {
287 24 : return NS_OK;
288 : }
289 :
290 0 : void OnError()
291 : {
292 0 : NS_ASSERTION(mTransaction->IsAborted(), "How else can this fail?!");
293 0 : }
294 :
295 24 : void ReleaseMainThreadObjects()
296 : {
297 24 : mObjectStore = nsnull;
298 24 : AsyncConnectionHelper::ReleaseMainThreadObjects();
299 24 : }
300 :
301 : private:
302 : // In-params
303 : nsString mName;
304 : nsRefPtr<IDBObjectStore> mObjectStore;
305 : };
306 :
307 : class GetAllHelper : public AsyncConnectionHelper
308 : {
309 : public:
310 17 : GetAllHelper(IDBTransaction* aTransaction,
311 : IDBRequest* aRequest,
312 : IDBObjectStore* aObjectStore,
313 : IDBKeyRange* aKeyRange,
314 : const PRUint32 aLimit)
315 : : AsyncConnectionHelper(aTransaction, aRequest), mObjectStore(aObjectStore),
316 17 : mKeyRange(aKeyRange), mLimit(aLimit)
317 17 : { }
318 :
319 34 : ~GetAllHelper()
320 34 : {
321 17 : for (PRUint32 index = 0; index < mCloneReadInfos.Length(); index++) {
322 : IDBObjectStore::ClearStructuredCloneBuffer(
323 0 : mCloneReadInfos[index].mCloneBuffer);
324 : }
325 68 : }
326 :
327 : nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
328 : nsresult GetSuccessResult(JSContext* aCx,
329 : jsval* aVal);
330 :
331 17 : void ReleaseMainThreadObjects()
332 : {
333 17 : mObjectStore = nsnull;
334 17 : mKeyRange = nsnull;
335 17 : for (PRUint32 index = 0; index < mCloneReadInfos.Length(); index++) {
336 : IDBObjectStore::ClearStructuredCloneBuffer(
337 0 : mCloneReadInfos[index].mCloneBuffer);
338 : }
339 17 : AsyncConnectionHelper::ReleaseMainThreadObjects();
340 17 : }
341 :
342 : protected:
343 : // In-params.
344 : nsRefPtr<IDBObjectStore> mObjectStore;
345 : nsRefPtr<IDBKeyRange> mKeyRange;
346 : const PRUint32 mLimit;
347 :
348 : private:
349 : // Out-params.
350 : nsTArray<StructuredCloneReadInfo> mCloneReadInfos;
351 : };
352 :
353 : class CountHelper : public AsyncConnectionHelper
354 128 : {
355 : public:
356 32 : CountHelper(IDBTransaction* aTransaction,
357 : IDBRequest* aRequest,
358 : IDBObjectStore* aObjectStore,
359 : IDBKeyRange* aKeyRange)
360 : : AsyncConnectionHelper(aTransaction, aRequest), mObjectStore(aObjectStore),
361 32 : mKeyRange(aKeyRange), mCount(0)
362 32 : { }
363 :
364 : nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
365 : nsresult GetSuccessResult(JSContext* aCx,
366 : jsval* aVal);
367 :
368 32 : void ReleaseMainThreadObjects()
369 : {
370 32 : mObjectStore = nsnull;
371 32 : mKeyRange = nsnull;
372 32 : AsyncConnectionHelper::ReleaseMainThreadObjects();
373 32 : }
374 :
375 : protected:
376 : nsRefPtr<IDBObjectStore> mObjectStore;
377 : nsRefPtr<IDBKeyRange> mKeyRange;
378 :
379 : private:
380 : PRUint64 mCount;
381 : };
382 :
383 : NS_STACK_CLASS
384 : class AutoRemoveIndex
385 : {
386 : public:
387 85 : AutoRemoveIndex(ObjectStoreInfo* aObjectStoreInfo,
388 : const nsAString& aIndexName)
389 85 : : mObjectStoreInfo(aObjectStoreInfo), mIndexName(aIndexName)
390 85 : { }
391 :
392 85 : ~AutoRemoveIndex()
393 85 : {
394 85 : if (mObjectStoreInfo) {
395 0 : for (PRUint32 i = 0; i < mObjectStoreInfo->indexes.Length(); i++) {
396 0 : if (mObjectStoreInfo->indexes[i].name == mIndexName) {
397 0 : mObjectStoreInfo->indexes.RemoveElementAt(i);
398 0 : break;
399 : }
400 : }
401 : }
402 85 : }
403 :
404 85 : void forget()
405 : {
406 85 : mObjectStoreInfo = nsnull;
407 85 : }
408 :
409 : private:
410 : ObjectStoreInfo* mObjectStoreInfo;
411 : nsString mIndexName;
412 : };
413 :
414 : inline
415 : bool
416 10223 : IgnoreWhitespace(PRUnichar c)
417 : {
418 10223 : return false;
419 : }
420 :
421 : typedef nsCharSeparatedTokenizerTemplate<IgnoreWhitespace> KeyPathTokenizer;
422 :
423 : inline
424 : nsresult
425 628 : GetJSValFromKeyPath(JSContext* aCx,
426 : jsval aVal,
427 : const nsAString& aKeyPath,
428 : jsval& aKey)
429 : {
430 628 : NS_ASSERTION(aCx, "Null pointer!");
431 : // aVal can be primitive iff the key path is empty.
432 628 : NS_ASSERTION(IDBObjectStore::IsValidKeyPath(aCx, aKeyPath),
433 : "This will explode!");
434 :
435 628 : KeyPathTokenizer tokenizer(aKeyPath, '.');
436 :
437 628 : jsval intermediate = aVal;
438 1879 : while (tokenizer.hasMoreTokens()) {
439 1246 : const nsDependentSubstring& token = tokenizer.nextToken();
440 :
441 623 : NS_ASSERTION(!token.IsEmpty(), "Should be a valid keypath");
442 :
443 623 : const jschar* keyPathChars = token.BeginReading();
444 623 : const size_t keyPathLen = token.Length();
445 :
446 623 : if (JSVAL_IS_PRIMITIVE(intermediate)) {
447 0 : intermediate = JSVAL_VOID;
448 : break;
449 : }
450 :
451 : JSBool ok = JS_GetUCProperty(aCx, JSVAL_TO_OBJECT(intermediate),
452 623 : keyPathChars, keyPathLen, &intermediate);
453 623 : NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
454 : }
455 :
456 628 : aKey = intermediate;
457 628 : return NS_OK;
458 : }
459 :
460 : inline
461 : nsresult
462 260 : GetKeyFromValue(JSContext* aCx,
463 : jsval aVal,
464 : const nsAString& aKeyPath,
465 : Key& aKey)
466 : {
467 : jsval key;
468 260 : nsresult rv = GetJSValFromKeyPath(aCx, aVal, aKeyPath, key);
469 260 : NS_ENSURE_SUCCESS(rv, rv);
470 :
471 260 : if (NS_FAILED(aKey.SetFromJSVal(aCx, key))) {
472 8 : aKey.Unset();
473 : }
474 :
475 260 : return NS_OK;
476 : }
477 :
478 : inline
479 : nsresult
480 0 : GetKeyFromValue(JSContext* aCx,
481 : jsval aVal,
482 : const nsTArray<nsString>& aKeyPathArray,
483 : Key& aKey)
484 : {
485 0 : NS_ASSERTION(!aKeyPathArray.IsEmpty(),
486 : "Should not use empty keyPath array");
487 0 : for (PRUint32 i = 0; i < aKeyPathArray.Length(); ++i) {
488 : jsval key;
489 0 : nsresult rv = GetJSValFromKeyPath(aCx, aVal, aKeyPathArray[i], key);
490 0 : NS_ENSURE_SUCCESS(rv, rv);
491 :
492 0 : if (NS_FAILED(aKey.AppendArrayItem(aCx, i == 0, key))) {
493 0 : NS_ASSERTION(aKey.IsUnset(), "Encoding error should unset");
494 0 : return NS_OK;
495 : }
496 : }
497 :
498 0 : aKey.FinishArray();
499 :
500 0 : return NS_OK;
501 : }
502 :
503 :
504 : inline
505 : already_AddRefed<IDBRequest>
506 2142 : GenerateRequest(IDBObjectStore* aObjectStore)
507 : {
508 2142 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
509 2142 : IDBDatabase* database = aObjectStore->Transaction()->Database();
510 : return IDBRequest::Create(aObjectStore, database,
511 2142 : aObjectStore->Transaction());
512 : }
513 :
514 : JSClass gDummyPropClass = {
515 : "dummy", 0,
516 : JS_PropertyStub, JS_PropertyStub,
517 : JS_PropertyStub, JS_StrictPropertyStub,
518 : JS_EnumerateStub, JS_ResolveStub,
519 : JS_ConvertStub, JS_FinalizeStub,
520 : JSCLASS_NO_OPTIONAL_MEMBERS
521 : };
522 :
523 : } // anonymous namespace
524 :
525 : // static
526 : already_AddRefed<IDBObjectStore>
527 667 : IDBObjectStore::Create(IDBTransaction* aTransaction,
528 : ObjectStoreInfo* aStoreInfo,
529 : nsIAtom* aDatabaseId)
530 : {
531 667 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
532 :
533 1334 : nsRefPtr<IDBObjectStore> objectStore = new IDBObjectStore();
534 :
535 667 : objectStore->mTransaction = aTransaction;
536 667 : objectStore->mName = aStoreInfo->name;
537 667 : objectStore->mId = aStoreInfo->id;
538 667 : objectStore->mKeyPath = aStoreInfo->keyPath;
539 667 : objectStore->mKeyPathArray = aStoreInfo->keyPathArray;
540 667 : objectStore->mAutoIncrement = !!aStoreInfo->nextAutoIncrementId;
541 667 : objectStore->mDatabaseId = aDatabaseId;
542 667 : objectStore->mInfo = aStoreInfo;
543 :
544 667 : return objectStore.forget();
545 : }
546 :
547 : // static
548 : bool
549 843 : IDBObjectStore::IsValidKeyPath(JSContext* aCx,
550 : const nsAString& aKeyPath)
551 : {
552 843 : NS_ASSERTION(!aKeyPath.IsVoid(), "What?");
553 :
554 843 : KeyPathTokenizer tokenizer(aKeyPath, '.');
555 :
556 2523 : while (tokenizer.hasMoreTokens()) {
557 1674 : nsString token(tokenizer.nextToken());
558 :
559 837 : if (!token.Length()) {
560 0 : return false;
561 : }
562 :
563 : jsval stringVal;
564 837 : if (!xpc::StringToJsval(aCx, token, &stringVal)) {
565 0 : return false;
566 : }
567 :
568 837 : NS_ASSERTION(JSVAL_IS_STRING(stringVal), "This should never happen");
569 837 : JSString* str = JSVAL_TO_STRING(stringVal);
570 :
571 837 : JSBool isIdentifier = JS_FALSE;
572 837 : if (!JS_IsIdentifier(aCx, str, &isIdentifier) || !isIdentifier) {
573 0 : return false;
574 : }
575 : }
576 :
577 : // If the very last character was a '.', the tokenizer won't give us an empty
578 : // token, but the keyPath is still invalid.
579 1680 : if (!aKeyPath.IsEmpty() &&
580 837 : aKeyPath.CharAt(aKeyPath.Length() - 1) == '.') {
581 0 : return false;
582 : }
583 :
584 843 : return true;
585 : }
586 :
587 : // static
588 : nsresult
589 368 : IDBObjectStore::AppendIndexUpdateInfo(PRInt64 aIndexID,
590 : const nsAString& aKeyPath,
591 : const nsTArray<nsString>& aKeyPathArray,
592 : bool aUnique,
593 : bool aMultiEntry,
594 : JSContext* aCx,
595 : jsval aVal,
596 : nsTArray<IndexUpdateInfo>& aUpdateInfoArray)
597 : {
598 : nsresult rv;
599 368 : if (!aKeyPathArray.IsEmpty()) {
600 0 : Key arrayKey;
601 0 : rv = GetKeyFromValue(aCx, aVal, aKeyPathArray, arrayKey);
602 0 : NS_ENSURE_SUCCESS(rv, rv);
603 :
604 0 : if (!arrayKey.IsUnset()) {
605 0 : IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement();
606 0 : updateInfo->indexId = aIndexID;
607 0 : updateInfo->indexUnique = aUnique;
608 0 : updateInfo->value = arrayKey;
609 : }
610 :
611 0 : return NS_OK;
612 : }
613 :
614 : jsval key;
615 368 : rv = GetJSValFromKeyPath(aCx, aVal, aKeyPath, key);
616 368 : NS_ENSURE_SUCCESS(rv, rv);
617 :
618 406 : if (aMultiEntry && !JSVAL_IS_PRIMITIVE(key) &&
619 38 : JS_IsArrayObject(aCx, JSVAL_TO_OBJECT(key))) {
620 35 : JSObject* array = JSVAL_TO_OBJECT(key);
621 : uint32_t arrayLength;
622 35 : if (!JS_GetArrayLength(aCx, array, &arrayLength)) {
623 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
624 : }
625 :
626 148 : for (uint32_t arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) {
627 : jsval arrayItem;
628 113 : if (!JS_GetElement(aCx, array, arrayIndex, &arrayItem)) {
629 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
630 : }
631 :
632 226 : Key value;
633 205 : if (NS_FAILED(value.SetFromJSVal(aCx, arrayItem)) ||
634 92 : value.IsUnset()) {
635 : // Not a value we can do anything with, ignore it.
636 30 : continue;
637 : }
638 :
639 83 : IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement();
640 83 : updateInfo->indexId = aIndexID;
641 83 : updateInfo->indexUnique = aUnique;
642 83 : updateInfo->value = value;
643 : }
644 : }
645 : else {
646 666 : Key value;
647 656 : if (NS_FAILED(value.SetFromJSVal(aCx, key)) ||
648 323 : value.IsUnset()) {
649 : // Not a value we can do anything with, ignore it.
650 43 : return NS_OK;
651 : }
652 :
653 290 : IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement();
654 290 : updateInfo->indexId = aIndexID;
655 290 : updateInfo->indexUnique = aUnique;
656 623 : updateInfo->value = value;
657 : }
658 :
659 325 : return NS_OK;
660 : }
661 :
662 : // static
663 : nsresult
664 309 : IDBObjectStore::UpdateIndexes(IDBTransaction* aTransaction,
665 : PRInt64 aObjectStoreId,
666 : const Key& aObjectStoreKey,
667 : bool aOverwrite,
668 : PRInt64 aObjectDataId,
669 : const nsTArray<IndexUpdateInfo>& aUpdateInfoArray)
670 : {
671 618 : nsCOMPtr<mozIStorageStatement> stmt;
672 : nsresult rv;
673 :
674 309 : NS_ASSERTION(aObjectDataId != LL_MININT, "Bad objectData id!");
675 :
676 618 : NS_NAMED_LITERAL_CSTRING(objectDataId, "object_data_id");
677 :
678 309 : if (aOverwrite) {
679 : stmt = aTransaction->GetCachedStatement(
680 : "DELETE FROM unique_index_data "
681 : "WHERE object_data_id = :object_data_id; "
682 : "DELETE FROM index_data "
683 60 : "WHERE object_data_id = :object_data_id");
684 :
685 120 : mozStorageStatementScoper scoper(stmt);
686 :
687 60 : rv = stmt->BindInt64ByName(objectDataId, aObjectDataId);
688 60 : NS_ENSURE_SUCCESS(rv, rv);
689 :
690 60 : rv = stmt->Execute();
691 60 : NS_ENSURE_SUCCESS(rv, rv);
692 : }
693 :
694 309 : PRUint32 infoCount = aUpdateInfoArray.Length();
695 671 : for (PRUint32 i = 0; i < infoCount; i++) {
696 369 : const IndexUpdateInfo& updateInfo = aUpdateInfoArray[i];
697 :
698 : // Insert new values.
699 :
700 : stmt = updateInfo.indexUnique ?
701 : aTransaction->GetCachedStatement(
702 : "INSERT INTO unique_index_data "
703 : "(index_id, object_data_id, object_data_key, value) "
704 : "VALUES (:index_id, :object_data_id, :object_data_key, :value)") :
705 : aTransaction->GetCachedStatement(
706 : "INSERT OR IGNORE INTO index_data ("
707 : "index_id, object_data_id, object_data_key, value) "
708 369 : "VALUES (:index_id, :object_data_id, :object_data_key, :value)");
709 :
710 369 : NS_ENSURE_TRUE(stmt, NS_ERROR_FAILURE);
711 :
712 738 : mozStorageStatementScoper scoper4(stmt);
713 :
714 738 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"),
715 369 : updateInfo.indexId);
716 369 : NS_ENSURE_SUCCESS(rv, rv);
717 :
718 369 : rv = stmt->BindInt64ByName(objectDataId, aObjectDataId);
719 369 : NS_ENSURE_SUCCESS(rv, rv);
720 :
721 : rv = aObjectStoreKey.BindToStatement(stmt,
722 369 : NS_LITERAL_CSTRING("object_data_key"));
723 369 : NS_ENSURE_SUCCESS(rv, rv);
724 :
725 369 : rv = updateInfo.value.BindToStatement(stmt, NS_LITERAL_CSTRING("value"));
726 369 : NS_ENSURE_SUCCESS(rv, rv);
727 :
728 369 : rv = stmt->Execute();
729 369 : if (rv == NS_ERROR_STORAGE_CONSTRAINT && updateInfo.indexUnique) {
730 : // If we're inserting multiple entries for the same unique index, then
731 : // we might have failed to insert due to colliding with another entry for
732 : // the same index in which case we should ignore it.
733 :
734 55 : for (PRInt32 j = (PRInt32)i - 1;
735 24 : j >= 0 && aUpdateInfoArray[j].indexId == updateInfo.indexId;
736 : --j) {
737 24 : if (updateInfo.value == aUpdateInfoArray[j].value) {
738 : // We found a key with the same value for the same index. So we
739 : // must have had a collision with a value we just inserted.
740 18 : rv = NS_OK;
741 18 : break;
742 : }
743 : }
744 : }
745 :
746 369 : if (NS_FAILED(rv)) {
747 7 : return rv;
748 : }
749 : }
750 :
751 302 : return NS_OK;
752 : }
753 :
754 : // static
755 : nsresult
756 1970 : IDBObjectStore::GetStructuredCloneReadInfoFromStatement(
757 : mozIStorageStatement* aStatement,
758 : PRUint32 aDataIndex,
759 : PRUint32 aFileIdsIndex,
760 : FileManager* aFileManager,
761 : StructuredCloneReadInfo& aInfo)
762 : {
763 : #ifdef DEBUG
764 : {
765 : PRInt32 type;
766 1970 : NS_ASSERTION(NS_SUCCEEDED(aStatement->GetTypeOfIndex(aDataIndex, &type)) &&
767 : type == mozIStorageStatement::VALUE_TYPE_BLOB,
768 : "Bad value type!");
769 : }
770 : #endif
771 :
772 : const PRUint8* blobData;
773 : PRUint32 blobDataLength;
774 : nsresult rv = aStatement->GetSharedBlob(aDataIndex, &blobDataLength,
775 1970 : &blobData);
776 1970 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
777 :
778 1970 : const char* compressed = reinterpret_cast<const char*>(blobData);
779 1970 : size_t compressedLength = size_t(blobDataLength);
780 :
781 : size_t uncompressedLength;
782 1970 : if (!snappy::GetUncompressedLength(compressed, compressedLength,
783 1970 : &uncompressedLength)) {
784 0 : NS_WARNING("Snappy can't determine uncompressed length!");
785 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
786 : }
787 :
788 5910 : nsAutoArrayPtr<char> uncompressed(new char[uncompressedLength]);
789 :
790 1970 : if (!snappy::RawUncompress(compressed, compressedLength,
791 1970 : uncompressed.get())) {
792 0 : NS_WARNING("Snappy can't determine uncompressed length!");
793 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
794 : }
795 :
796 1970 : JSAutoStructuredCloneBuffer& buffer = aInfo.mCloneBuffer;
797 3940 : if (!buffer.copy(reinterpret_cast<const uint64_t *>(uncompressed.get()),
798 3940 : uncompressedLength)) {
799 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
800 : }
801 :
802 : bool isNull;
803 1970 : rv = aStatement->GetIsNull(aFileIdsIndex, &isNull);
804 1970 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
805 :
806 1970 : if (isNull) {
807 1970 : return NS_OK;
808 : }
809 :
810 0 : nsString ids;
811 0 : rv = aStatement->GetString(aFileIdsIndex, ids);
812 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
813 :
814 0 : nsAutoTArray<PRInt64, 10> array;
815 0 : rv = ConvertFileIdsToArray(ids, array);
816 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
817 :
818 0 : for (PRUint32 i = 0; i < array.Length(); i++) {
819 0 : const PRInt64& id = array.ElementAt(i);
820 :
821 0 : nsRefPtr<FileInfo> fileInfo = aFileManager->GetFileInfo(id);
822 0 : NS_ASSERTION(fileInfo, "Null file info!");
823 :
824 0 : aInfo.mFileInfos.AppendElement(fileInfo);
825 : }
826 :
827 0 : return NS_OK;
828 : }
829 :
830 : // static
831 : void
832 6214 : IDBObjectStore::ClearStructuredCloneBuffer(JSAutoStructuredCloneBuffer& aBuffer)
833 : {
834 6214 : if (aBuffer.data()) {
835 63 : aBuffer.clear();
836 : }
837 6214 : }
838 :
839 : // static
840 : bool
841 623 : IDBObjectStore::DeserializeValue(JSContext* aCx,
842 : StructuredCloneReadInfo& aCloneReadInfo,
843 : jsval* aValue)
844 : {
845 623 : NS_ASSERTION(NS_IsMainThread(),
846 : "Should only be deserializing on the main thread!");
847 623 : NS_ASSERTION(aCx, "A JSContext is required!");
848 :
849 623 : JSAutoStructuredCloneBuffer& buffer = aCloneReadInfo.mCloneBuffer;
850 :
851 623 : if (!buffer.data()) {
852 13 : *aValue = JSVAL_VOID;
853 13 : return true;
854 : }
855 :
856 1220 : JSAutoRequest ar(aCx);
857 :
858 : JSStructuredCloneCallbacks callbacks = {
859 : IDBObjectStore::StructuredCloneReadCallback,
860 : nsnull,
861 : nsnull
862 610 : };
863 :
864 610 : return buffer.read(aCx, aValue, &callbacks, &aCloneReadInfo);
865 : }
866 :
867 : // static
868 : bool
869 1837 : IDBObjectStore::SerializeValue(JSContext* aCx,
870 : StructuredCloneWriteInfo& aCloneWriteInfo,
871 : jsval aValue)
872 : {
873 1837 : NS_ASSERTION(NS_IsMainThread(),
874 : "Should only be serializing on the main thread!");
875 1837 : NS_ASSERTION(aCx, "A JSContext is required!");
876 :
877 3674 : JSAutoRequest ar(aCx);
878 :
879 : JSStructuredCloneCallbacks callbacks = {
880 : nsnull,
881 : StructuredCloneWriteCallback,
882 : nsnull
883 1837 : };
884 :
885 1837 : JSAutoStructuredCloneBuffer& buffer = aCloneWriteInfo.mCloneBuffer;
886 :
887 1837 : return buffer.write(aCx, aValue, &callbacks, &aCloneWriteInfo);
888 : }
889 :
890 : static inline PRUint32
891 0 : SwapBytes(PRUint32 u)
892 : {
893 : #ifdef IS_BIG_ENDIAN
894 : return ((u & 0x000000ffU) << 24) |
895 : ((u & 0x0000ff00U) << 8) |
896 : ((u & 0x00ff0000U) >> 8) |
897 : ((u & 0xff000000U) >> 24);
898 : #else
899 0 : return u;
900 : #endif
901 : }
902 :
903 : static inline double
904 16 : SwapBytes(PRUint64 u)
905 : {
906 : #ifdef IS_BIG_ENDIAN
907 : return ((u & 0x00000000000000ffLLU) << 56) |
908 : ((u & 0x000000000000ff00LLU) << 40) |
909 : ((u & 0x0000000000ff0000LLU) << 24) |
910 : ((u & 0x00000000ff000000LLU) << 8) |
911 : ((u & 0x000000ff00000000LLU) >> 8) |
912 : ((u & 0x0000ff0000000000LLU) >> 24) |
913 : ((u & 0x00ff000000000000LLU) >> 40) |
914 : ((u & 0xff00000000000000LLU) >> 56);
915 : #else
916 16 : return double(u);
917 : #endif
918 : }
919 :
920 : static inline bool
921 0 : StructuredCloneReadString(JSStructuredCloneReader* aReader,
922 : nsCString& aString)
923 : {
924 : PRUint32 length;
925 0 : if (!JS_ReadBytes(aReader, &length, sizeof(PRUint32))) {
926 0 : NS_WARNING("Failed to read length!");
927 0 : return false;
928 : }
929 0 : length = SwapBytes(length);
930 :
931 0 : if (!EnsureStringLength(aString, length)) {
932 0 : NS_WARNING("Out of memory?");
933 0 : return false;
934 : }
935 0 : char* buffer = aString.BeginWriting();
936 :
937 0 : if (!JS_ReadBytes(aReader, buffer, length)) {
938 0 : NS_WARNING("Failed to read type!");
939 0 : return false;
940 : }
941 :
942 0 : return true;
943 : }
944 :
945 : JSObject*
946 0 : IDBObjectStore::StructuredCloneReadCallback(JSContext* aCx,
947 : JSStructuredCloneReader* aReader,
948 : uint32_t aTag,
949 : uint32_t aData,
950 : void* aClosure)
951 : {
952 0 : if (aTag == SCTAG_DOM_BLOB || aTag == SCTAG_DOM_FILE) {
953 : StructuredCloneReadInfo* cloneReadInfo =
954 0 : reinterpret_cast<StructuredCloneReadInfo*>(aClosure);
955 :
956 0 : NS_ASSERTION(aData < cloneReadInfo->mFileInfos.Length(),
957 : "Bad blob index!");
958 :
959 0 : nsRefPtr<FileInfo> fileInfo = cloneReadInfo->mFileInfos[aData];
960 0 : nsRefPtr<FileManager> fileManager = fileInfo->Manager();
961 0 : nsCOMPtr<nsIFile> directory = fileManager->GetDirectory();
962 0 : if (!directory) {
963 0 : return nsnull;
964 : }
965 :
966 : nsCOMPtr<nsIFile> nativeFile =
967 0 : fileManager->GetFileForId(directory, fileInfo->Id());
968 0 : if (!nativeFile) {
969 0 : return nsnull;
970 : }
971 :
972 : PRUint64 size;
973 0 : if (!JS_ReadBytes(aReader, &size, sizeof(PRUint64))) {
974 0 : NS_WARNING("Failed to read size!");
975 0 : return nsnull;
976 : }
977 0 : size = SwapBytes(size);
978 :
979 0 : nsCString type;
980 0 : if (!StructuredCloneReadString(aReader, type)) {
981 0 : return nsnull;
982 : }
983 0 : NS_ConvertUTF8toUTF16 convType(type);
984 :
985 0 : if (aTag == SCTAG_DOM_BLOB) {
986 : nsCOMPtr<nsIDOMBlob> blob = new nsDOMFileFile(convType, size,
987 0 : nativeFile, fileInfo);
988 :
989 : jsval wrappedBlob;
990 : nsresult rv =
991 : nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx), blob,
992 0 : &NS_GET_IID(nsIDOMBlob), &wrappedBlob);
993 0 : if (NS_FAILED(rv)) {
994 0 : NS_WARNING("Failed to wrap native!");
995 0 : return nsnull;
996 : }
997 :
998 0 : return JSVAL_TO_OBJECT(wrappedBlob);
999 : }
1000 :
1001 0 : nsCString name;
1002 0 : if (!StructuredCloneReadString(aReader, name)) {
1003 0 : return nsnull;
1004 : }
1005 0 : NS_ConvertUTF8toUTF16 convName(name);
1006 :
1007 : nsCOMPtr<nsIDOMFile> file = new nsDOMFileFile(convName, convType, size,
1008 0 : nativeFile, fileInfo);
1009 :
1010 : jsval wrappedFile;
1011 : nsresult rv =
1012 : nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx), file,
1013 0 : &NS_GET_IID(nsIDOMFile), &wrappedFile);
1014 0 : if (NS_FAILED(rv)) {
1015 0 : NS_WARNING("Failed to wrap native!");
1016 0 : return nsnull;
1017 : }
1018 :
1019 0 : return JSVAL_TO_OBJECT(wrappedFile);
1020 : }
1021 :
1022 : const JSStructuredCloneCallbacks* runtimeCallbacks =
1023 0 : js::GetContextStructuredCloneCallbacks(aCx);
1024 :
1025 0 : if (runtimeCallbacks) {
1026 0 : return runtimeCallbacks->read(aCx, aReader, aTag, aData, nsnull);
1027 : }
1028 :
1029 0 : return nsnull;
1030 : }
1031 :
1032 : JSBool
1033 16 : IDBObjectStore::StructuredCloneWriteCallback(JSContext* aCx,
1034 : JSStructuredCloneWriter* aWriter,
1035 : JSObject* aObj,
1036 : void* aClosure)
1037 : {
1038 : StructuredCloneWriteInfo* cloneWriteInfo =
1039 16 : reinterpret_cast<StructuredCloneWriteInfo*>(aClosure);
1040 :
1041 16 : if (JS_GetClass(aObj) == &gDummyPropClass) {
1042 16 : NS_ASSERTION(cloneWriteInfo->mOffsetToKeyProp == 0,
1043 : "We should not have been here before!");
1044 16 : cloneWriteInfo->mOffsetToKeyProp = js_GetSCOffset(aWriter);
1045 :
1046 16 : PRUint64 value = 0;
1047 16 : return JS_WriteBytes(aWriter, &value, sizeof(value));
1048 : }
1049 :
1050 0 : nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
1051 0 : nsContentUtils::XPConnect()->
1052 0 : GetWrappedNativeOfJSObject(aCx, aObj, getter_AddRefs(wrappedNative));
1053 :
1054 0 : if (wrappedNative) {
1055 0 : nsISupports* supports = wrappedNative->Native();
1056 :
1057 0 : nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(supports);
1058 0 : if (blob) {
1059 0 : nsCOMPtr<nsIDOMFile> file = do_QueryInterface(blob);
1060 :
1061 : PRUint64 size;
1062 0 : if (NS_FAILED(blob->GetSize(&size))) {
1063 0 : return false;
1064 : }
1065 0 : size = SwapBytes(size);
1066 :
1067 0 : nsString type;
1068 0 : if (NS_FAILED(blob->GetType(type))) {
1069 0 : return false;
1070 : }
1071 0 : NS_ConvertUTF16toUTF8 convType(type);
1072 0 : PRUint32 convTypeLength = SwapBytes(convType.Length());
1073 :
1074 0 : if (!JS_WriteUint32Pair(aWriter, file ? SCTAG_DOM_FILE : SCTAG_DOM_BLOB,
1075 0 : cloneWriteInfo->mBlobs.Length()) ||
1076 0 : !JS_WriteBytes(aWriter, &size, sizeof(PRUint64)) ||
1077 0 : !JS_WriteBytes(aWriter, &convTypeLength, sizeof(PRUint32)) ||
1078 0 : !JS_WriteBytes(aWriter, convType.get(), convType.Length())) {
1079 0 : return false;
1080 : }
1081 :
1082 0 : if (file) {
1083 0 : nsString name;
1084 0 : if (NS_FAILED(file->GetName(name))) {
1085 0 : return false;
1086 : }
1087 0 : NS_ConvertUTF16toUTF8 convName(name);
1088 0 : PRUint32 convNameLength = SwapBytes(convName.Length());
1089 :
1090 0 : if (!JS_WriteBytes(aWriter, &convNameLength, sizeof(PRUint32)) ||
1091 0 : !JS_WriteBytes(aWriter, convName.get(), convName.Length())) {
1092 0 : return false;
1093 : }
1094 : }
1095 :
1096 0 : cloneWriteInfo->mBlobs.AppendElement(blob);
1097 :
1098 0 : return true;
1099 : }
1100 : }
1101 :
1102 : // try using the runtime callbacks
1103 : const JSStructuredCloneCallbacks* runtimeCallbacks =
1104 0 : js::GetContextStructuredCloneCallbacks(aCx);
1105 0 : if (runtimeCallbacks) {
1106 0 : return runtimeCallbacks->write(aCx, aWriter, aObj, nsnull);
1107 : }
1108 :
1109 0 : return false;
1110 : }
1111 :
1112 : nsresult
1113 0 : IDBObjectStore::ConvertFileIdsToArray(const nsAString& aFileIds,
1114 : nsTArray<PRInt64>& aResult)
1115 : {
1116 0 : nsCharSeparatedTokenizerTemplate<IgnoreWhitespace> tokenizer(aFileIds, ' ');
1117 :
1118 0 : while (tokenizer.hasMoreTokens()) {
1119 0 : nsString token(tokenizer.nextToken());
1120 :
1121 0 : NS_ASSERTION(!token.IsEmpty(), "Should be a valid id!");
1122 :
1123 : nsresult rv;
1124 0 : PRInt32 id = token.ToInteger(&rv);
1125 0 : NS_ENSURE_SUCCESS(rv, rv);
1126 :
1127 0 : PRInt64* element = aResult.AppendElement();
1128 0 : *element = id;
1129 : }
1130 :
1131 0 : return NS_OK;
1132 : }
1133 :
1134 667 : IDBObjectStore::IDBObjectStore()
1135 : : mId(LL_MININT),
1136 667 : mAutoIncrement(false)
1137 : {
1138 667 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1139 667 : }
1140 :
1141 1334 : IDBObjectStore::~IDBObjectStore()
1142 : {
1143 667 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1144 667 : }
1145 :
1146 : nsresult
1147 1956 : IDBObjectStore::GetAddInfo(JSContext* aCx,
1148 : jsval aValue,
1149 : jsval aKeyVal,
1150 : StructuredCloneWriteInfo& aCloneWriteInfo,
1151 : Key& aKey,
1152 : nsTArray<IndexUpdateInfo>& aUpdateInfoArray)
1153 : {
1154 : nsresult rv;
1155 :
1156 : // Return DATA_ERR if a key was passed in and this objectStore uses inline
1157 : // keys.
1158 1956 : if (!JSVAL_IS_VOID(aKeyVal) && HasKeyPath()) {
1159 53 : return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
1160 : }
1161 :
1162 3806 : JSAutoRequest ar(aCx);
1163 :
1164 1903 : if (!HasKeyPath()) {
1165 : // Out-of-line keys must be passed in.
1166 1561 : rv = aKey.SetFromJSVal(aCx, aKeyVal);
1167 1561 : NS_ENSURE_SUCCESS(rv, rv);
1168 : }
1169 342 : else if (!mAutoIncrement) {
1170 : // Inline keys live on the object. Make sure that the value passed in is an
1171 : // object.
1172 260 : if (UsesKeyPathArray()) {
1173 0 : rv = GetKeyFromValue(aCx, aValue, mKeyPathArray, aKey);
1174 : }
1175 : else {
1176 260 : rv = GetKeyFromValue(aCx, aValue, mKeyPath, aKey);
1177 : }
1178 260 : NS_ENSURE_SUCCESS(rv, rv);
1179 : }
1180 :
1181 : // Return DATA_ERR if no key was specified this isn't an autoIncrement
1182 : // objectStore.
1183 1877 : if (aKey.IsUnset() && !mAutoIncrement) {
1184 32 : return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
1185 : }
1186 :
1187 : // Figure out indexes and the index values to update here.
1188 1845 : PRUint32 count = mInfo->indexes.Length();
1189 1845 : aUpdateInfoArray.SetCapacity(count); // Pretty good estimate
1190 2076 : for (PRUint32 indexesIndex = 0; indexesIndex < count; indexesIndex++) {
1191 231 : const IndexInfo& indexInfo = mInfo->indexes[indexesIndex];
1192 :
1193 : rv = AppendIndexUpdateInfo(indexInfo.id, indexInfo.keyPath,
1194 : indexInfo.keyPathArray, indexInfo.unique,
1195 : indexInfo.multiEntry, aCx, aValue,
1196 231 : aUpdateInfoArray);
1197 231 : NS_ENSURE_SUCCESS(rv, rv);
1198 : }
1199 :
1200 3690 : nsString targetObjectPropName;
1201 1845 : JSObject* targetObject = nsnull;
1202 :
1203 1845 : rv = NS_OK;
1204 1845 : if (mAutoIncrement && HasKeyPath()) {
1205 82 : NS_ASSERTION(aKey.IsUnset(), "Shouldn't have gotten the key yet!");
1206 :
1207 82 : if (JSVAL_IS_PRIMITIVE(aValue)) {
1208 0 : return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
1209 : }
1210 :
1211 82 : KeyPathTokenizer tokenizer(mKeyPath, '.');
1212 82 : NS_ASSERTION(tokenizer.hasMoreTokens(),
1213 : "Shouldn't have empty keypath and autoincrement");
1214 :
1215 82 : JSObject* obj = JSVAL_TO_OBJECT(aValue);
1216 238 : while (tokenizer.hasMoreTokens()) {
1217 164 : const nsDependentSubstring& token = tokenizer.nextToken();
1218 :
1219 82 : NS_ASSERTION(!token.IsEmpty(), "Should be a valid keypath");
1220 :
1221 82 : const jschar* keyPathChars = token.BeginReading();
1222 82 : const size_t keyPathLen = token.Length();
1223 :
1224 : JSBool hasProp;
1225 82 : if (!targetObject) {
1226 : // We're still walking the chain of existing objects
1227 :
1228 : JSBool ok = JS_HasUCProperty(aCx, obj, keyPathChars, keyPathLen,
1229 82 : &hasProp);
1230 82 : NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1231 :
1232 82 : if (hasProp) {
1233 : // Get if the property exists...
1234 : jsval intermediate;
1235 : JSBool ok = JS_GetUCProperty(aCx, obj, keyPathChars, keyPathLen,
1236 66 : &intermediate);
1237 66 : NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1238 :
1239 66 : if (tokenizer.hasMoreTokens()) {
1240 : // ...and walk to it if there are more steps...
1241 0 : if (JSVAL_IS_PRIMITIVE(intermediate)) {
1242 0 : return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
1243 : }
1244 0 : obj = JSVAL_TO_OBJECT(intermediate);
1245 : }
1246 : else {
1247 : // ...otherwise use it as key
1248 66 : aKey.SetFromJSVal(aCx, intermediate);
1249 66 : if (aKey.IsUnset()) {
1250 8 : return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
1251 : }
1252 : }
1253 : }
1254 : else {
1255 : // If the property doesn't exist, fall into below path of starting
1256 : // to define properties
1257 16 : targetObject = obj;
1258 16 : targetObjectPropName = token;
1259 : }
1260 : }
1261 :
1262 74 : if (targetObject) {
1263 : // We have started inserting new objects or are about to just insert
1264 : // the first one.
1265 16 : if (tokenizer.hasMoreTokens()) {
1266 : // If we're not at the end, we need to add a dummy object to the chain.
1267 0 : JSObject* dummy = JS_NewObject(aCx, nsnull, nsnull, nsnull);
1268 0 : if (!dummy) {
1269 0 : rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1270 : break;
1271 : }
1272 :
1273 0 : if (!JS_DefineUCProperty(aCx, obj, token.BeginReading(),
1274 : token.Length(),
1275 : OBJECT_TO_JSVAL(dummy), nsnull, nsnull,
1276 0 : JSPROP_ENUMERATE)) {
1277 0 : rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1278 : break;
1279 : }
1280 :
1281 0 : obj = dummy;
1282 : }
1283 : else {
1284 16 : JSObject* dummy = JS_NewObject(aCx, &gDummyPropClass, nsnull, nsnull);
1285 16 : if (!dummy) {
1286 0 : rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1287 : break;
1288 : }
1289 :
1290 16 : if (!JS_DefineUCProperty(aCx, obj, token.BeginReading(),
1291 : token.Length(), OBJECT_TO_JSVAL(dummy),
1292 16 : nsnull, nsnull, JSPROP_ENUMERATE)) {
1293 0 : rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1294 : break;
1295 : }
1296 : }
1297 : }
1298 : }
1299 : }
1300 :
1301 1837 : aCloneWriteInfo.mOffsetToKeyProp = 0;
1302 :
1303 : // We guard on rv being a success because we need to run the property
1304 : // deletion code below even if we should not be serializing the value
1305 3674 : if (NS_SUCCEEDED(rv) &&
1306 1837 : !IDBObjectStore::SerializeValue(aCx, aCloneWriteInfo, aValue)) {
1307 0 : rv = NS_ERROR_DOM_DATA_CLONE_ERR;
1308 : }
1309 :
1310 1837 : if (targetObject) {
1311 : // If this fails, we lose, and the web page sees a magical property
1312 : // appear on the object :-(
1313 : jsval succeeded;
1314 16 : if (!JS_DeleteUCProperty2(aCx, targetObject,
1315 : targetObjectPropName.get(),
1316 16 : targetObjectPropName.Length(), &succeeded)) {
1317 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1318 : }
1319 16 : NS_ASSERTION(JSVAL_IS_BOOLEAN(succeeded), "Wtf?");
1320 16 : NS_ENSURE_TRUE(JSVAL_TO_BOOLEAN(succeeded),
1321 : NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1322 : }
1323 :
1324 1837 : return rv;
1325 : }
1326 :
1327 : nsresult
1328 1959 : IDBObjectStore::AddOrPut(const jsval& aValue,
1329 : const jsval& aKey,
1330 : JSContext* aCx,
1331 : PRUint8 aOptionalArgCount,
1332 : nsIIDBRequest** _retval,
1333 : bool aOverwrite)
1334 : {
1335 1959 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1336 :
1337 1959 : if (!mTransaction->IsOpen()) {
1338 3 : return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
1339 : }
1340 :
1341 1956 : if (!IsWriteAllowed()) {
1342 0 : return NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR;
1343 : }
1344 :
1345 1956 : jsval keyval = (aOptionalArgCount >= 1) ? aKey : JSVAL_VOID;
1346 :
1347 3912 : StructuredCloneWriteInfo cloneWriteInfo;
1348 3912 : Key key;
1349 3912 : nsTArray<IndexUpdateInfo> updateInfo;
1350 :
1351 : nsresult rv = GetAddInfo(aCx, aValue, keyval, cloneWriteInfo, key,
1352 1956 : updateInfo);
1353 1956 : if (NS_FAILED(rv)) {
1354 119 : return rv;
1355 : }
1356 :
1357 3674 : nsRefPtr<IDBRequest> request = GenerateRequest(this);
1358 1837 : NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1359 :
1360 : nsRefPtr<AddHelper> helper =
1361 : new AddHelper(mTransaction, request, this, cloneWriteInfo, key, aOverwrite,
1362 5511 : updateInfo);
1363 :
1364 1837 : rv = helper->DispatchToTransactionPool();
1365 1837 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1366 :
1367 1837 : request.forget(_retval);
1368 1837 : return NS_OK;
1369 : }
1370 :
1371 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(IDBObjectStore)
1372 :
1373 544 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBObjectStore)
1374 544 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mTransaction,
1375 : nsIDOMEventTarget)
1376 :
1377 649 : for (PRUint32 i = 0; i < tmp->mCreatedIndexes.Length(); i++) {
1378 105 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCreatedIndexes[i]");
1379 105 : cb.NoteXPCOMChild(static_cast<nsIIDBIndex*>(tmp->mCreatedIndexes[i].get()));
1380 : }
1381 544 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1382 :
1383 544 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBObjectStore)
1384 : // Don't unlink mTransaction!
1385 :
1386 544 : tmp->mCreatedIndexes.Clear();
1387 544 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1388 :
1389 20350 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBObjectStore)
1390 10027 : NS_INTERFACE_MAP_ENTRY(nsIIDBObjectStore)
1391 6022 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBObjectStore)
1392 5355 : NS_INTERFACE_MAP_ENTRY(nsISupports)
1393 4674 : NS_INTERFACE_MAP_END
1394 :
1395 11744 : NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBObjectStore)
1396 12411 : NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBObjectStore)
1397 :
1398 : DOMCI_DATA(IDBObjectStore, IDBObjectStore)
1399 :
1400 : NS_IMETHODIMP
1401 28 : IDBObjectStore::GetName(nsAString& aName)
1402 : {
1403 28 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1404 :
1405 28 : aName.Assign(mName);
1406 28 : return NS_OK;
1407 : }
1408 :
1409 : NS_IMETHODIMP
1410 20 : IDBObjectStore::GetKeyPath(JSContext* aCx,
1411 : jsval* aVal)
1412 : {
1413 20 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1414 :
1415 20 : if (UsesKeyPathArray()) {
1416 0 : JSObject* array = JS_NewArrayObject(aCx, mKeyPathArray.Length(), nsnull);
1417 0 : if (!array) {
1418 0 : NS_WARNING("Failed to make array!");
1419 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1420 : }
1421 :
1422 0 : for (PRUint32 i = 0; i < mKeyPathArray.Length(); ++i) {
1423 : jsval val;
1424 0 : nsString tmp(mKeyPathArray[i]);
1425 0 : if (!xpc::StringToJsval(aCx, tmp, &val)) {
1426 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1427 : }
1428 :
1429 0 : if (!JS_SetElement(aCx, array, i, &val)) {
1430 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1431 : }
1432 : }
1433 :
1434 0 : *aVal = OBJECT_TO_JSVAL(array);
1435 : }
1436 : else {
1437 40 : nsString tmp(mKeyPath);
1438 20 : if (!xpc::StringToJsval(aCx, tmp, aVal)) {
1439 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1440 : }
1441 : }
1442 20 : return NS_OK;
1443 : }
1444 :
1445 : NS_IMETHODIMP
1446 1 : IDBObjectStore::GetTransaction(nsIIDBTransaction** aTransaction)
1447 : {
1448 1 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1449 :
1450 2 : nsCOMPtr<nsIIDBTransaction> transaction(mTransaction);
1451 1 : transaction.forget(aTransaction);
1452 1 : return NS_OK;
1453 : }
1454 :
1455 : NS_IMETHODIMP
1456 0 : IDBObjectStore::GetAutoIncrement(bool* aAutoIncrement)
1457 : {
1458 0 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1459 :
1460 0 : *aAutoIncrement = mAutoIncrement;
1461 0 : return NS_OK;
1462 : }
1463 :
1464 : NS_IMETHODIMP
1465 150 : IDBObjectStore::GetIndexNames(nsIDOMDOMStringList** aIndexNames)
1466 : {
1467 150 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1468 :
1469 300 : nsRefPtr<nsDOMStringList> list(new nsDOMStringList());
1470 :
1471 150 : PRUint32 count = mInfo->indexes.Length();
1472 643 : for (PRUint32 index = 0; index < count; index++) {
1473 493 : NS_ENSURE_TRUE(list->Add(mInfo->indexes[index].name),
1474 : NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1475 : }
1476 :
1477 150 : list.forget(aIndexNames);
1478 150 : return NS_OK;
1479 : }
1480 :
1481 : NS_IMETHODIMP
1482 176 : IDBObjectStore::Get(const jsval& aKey,
1483 : JSContext* aCx,
1484 : nsIIDBRequest** _retval)
1485 : {
1486 176 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1487 :
1488 176 : if (!mTransaction->IsOpen()) {
1489 1 : return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
1490 : }
1491 :
1492 350 : nsRefPtr<IDBKeyRange> keyRange;
1493 175 : nsresult rv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
1494 175 : NS_ENSURE_SUCCESS(rv, rv);
1495 :
1496 175 : if (!keyRange) {
1497 : // Must specify a key or keyRange for get().
1498 2 : return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
1499 : }
1500 :
1501 346 : nsRefPtr<IDBRequest> request = GenerateRequest(this);
1502 173 : NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1503 :
1504 : nsRefPtr<GetHelper> helper =
1505 519 : new GetHelper(mTransaction, request, this, keyRange);
1506 :
1507 173 : rv = helper->DispatchToTransactionPool();
1508 173 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1509 :
1510 173 : request.forget(_retval);
1511 173 : return NS_OK;
1512 : }
1513 :
1514 : NS_IMETHODIMP
1515 18 : IDBObjectStore::GetAll(const jsval& aKey,
1516 : PRUint32 aLimit,
1517 : JSContext* aCx,
1518 : PRUint8 aOptionalArgCount,
1519 : nsIIDBRequest** _retval)
1520 : {
1521 18 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1522 :
1523 18 : if (!mTransaction->IsOpen()) {
1524 1 : return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
1525 : }
1526 :
1527 : nsresult rv;
1528 :
1529 34 : nsRefPtr<IDBKeyRange> keyRange;
1530 17 : if (aOptionalArgCount) {
1531 14 : rv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
1532 14 : NS_ENSURE_SUCCESS(rv, rv);
1533 : }
1534 :
1535 17 : if (aOptionalArgCount < 2 || aLimit == 0) {
1536 14 : aLimit = PR_UINT32_MAX;
1537 : }
1538 :
1539 34 : nsRefPtr<IDBRequest> request = GenerateRequest(this);
1540 17 : NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1541 :
1542 : nsRefPtr<GetAllHelper> helper =
1543 51 : new GetAllHelper(mTransaction, request, this, keyRange, aLimit);
1544 :
1545 17 : rv = helper->DispatchToTransactionPool();
1546 17 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1547 :
1548 17 : request.forget(_retval);
1549 17 : return NS_OK;
1550 : }
1551 :
1552 : NS_IMETHODIMP
1553 1827 : IDBObjectStore::Add(const jsval& aValue,
1554 : const jsval& aKey,
1555 : JSContext* aCx,
1556 : PRUint8 aOptionalArgCount,
1557 : nsIIDBRequest** _retval)
1558 : {
1559 1827 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1560 :
1561 1827 : return AddOrPut(aValue, aKey, aCx, aOptionalArgCount, _retval, false);
1562 : }
1563 :
1564 : NS_IMETHODIMP
1565 132 : IDBObjectStore::Put(const jsval& aValue,
1566 : const jsval& aKey,
1567 : JSContext* aCx,
1568 : PRUint8 aOptionalArgCount,
1569 : nsIIDBRequest** _retval)
1570 : {
1571 132 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1572 :
1573 132 : return AddOrPut(aValue, aKey, aCx, aOptionalArgCount, _retval, true);
1574 : }
1575 :
1576 : NS_IMETHODIMP
1577 30 : IDBObjectStore::Delete(const jsval& aKey,
1578 : JSContext* aCx,
1579 : nsIIDBRequest** _retval)
1580 : {
1581 30 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1582 :
1583 30 : if (!mTransaction->IsOpen()) {
1584 1 : return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
1585 : }
1586 :
1587 29 : if (!IsWriteAllowed()) {
1588 0 : return NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR;
1589 : }
1590 :
1591 58 : nsRefPtr<IDBKeyRange> keyRange;
1592 29 : nsresult rv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
1593 29 : NS_ENSURE_SUCCESS(rv, rv);
1594 :
1595 28 : if (!keyRange) {
1596 : // Must specify a key or keyRange for delete().
1597 2 : return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
1598 : }
1599 :
1600 52 : nsRefPtr<IDBRequest> request = GenerateRequest(this);
1601 26 : NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1602 :
1603 : nsRefPtr<DeleteHelper> helper =
1604 78 : new DeleteHelper(mTransaction, request, this, keyRange);
1605 :
1606 26 : rv = helper->DispatchToTransactionPool();
1607 26 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1608 :
1609 26 : request.forget(_retval);
1610 26 : return NS_OK;
1611 : }
1612 :
1613 : NS_IMETHODIMP
1614 11 : IDBObjectStore::Clear(nsIIDBRequest** _retval)
1615 : {
1616 11 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1617 :
1618 11 : if (!mTransaction->IsOpen()) {
1619 0 : return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
1620 : }
1621 :
1622 11 : if (!IsWriteAllowed()) {
1623 1 : return NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR;
1624 : }
1625 :
1626 20 : nsRefPtr<IDBRequest> request = GenerateRequest(this);
1627 10 : NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1628 :
1629 30 : nsRefPtr<ClearHelper> helper(new ClearHelper(mTransaction, request, this));
1630 :
1631 10 : nsresult rv = helper->DispatchToTransactionPool();
1632 10 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1633 :
1634 10 : request.forget(_retval);
1635 10 : return NS_OK;
1636 : }
1637 :
1638 : NS_IMETHODIMP
1639 48 : IDBObjectStore::OpenCursor(const jsval& aKey,
1640 : const nsAString& aDirection,
1641 : JSContext* aCx,
1642 : PRUint8 aOptionalArgCount,
1643 : nsIIDBRequest** _retval)
1644 : {
1645 48 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1646 :
1647 48 : if (!mTransaction->IsOpen()) {
1648 1 : return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
1649 : }
1650 :
1651 : nsresult rv;
1652 :
1653 47 : IDBCursor::Direction direction = IDBCursor::NEXT;
1654 :
1655 94 : nsRefPtr<IDBKeyRange> keyRange;
1656 47 : if (aOptionalArgCount) {
1657 16 : rv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
1658 16 : NS_ENSURE_SUCCESS(rv, rv);
1659 :
1660 16 : if (aOptionalArgCount >= 2) {
1661 4 : rv = IDBCursor::ParseDirection(aDirection, &direction);
1662 4 : NS_ENSURE_SUCCESS(rv, rv);
1663 : }
1664 : }
1665 :
1666 94 : nsRefPtr<IDBRequest> request = GenerateRequest(this);
1667 47 : NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1668 :
1669 : nsRefPtr<OpenCursorHelper> helper =
1670 141 : new OpenCursorHelper(mTransaction, request, this, keyRange, direction);
1671 :
1672 47 : rv = helper->DispatchToTransactionPool();
1673 47 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1674 :
1675 47 : request.forget(_retval);
1676 47 : return NS_OK;
1677 : }
1678 :
1679 : NS_IMETHODIMP
1680 92 : IDBObjectStore::CreateIndex(const nsAString& aName,
1681 : const jsval& aKeyPath,
1682 : const jsval& aOptions,
1683 : JSContext* aCx,
1684 : nsIIDBIndex** _retval)
1685 : {
1686 92 : NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
1687 :
1688 : // Get KeyPath
1689 184 : nsString keyPath;
1690 184 : nsTArray<nsString> keyPathArray;
1691 :
1692 : // See if this is a JS array.
1693 96 : if (!JSVAL_IS_PRIMITIVE(aKeyPath) &&
1694 4 : JS_IsArrayObject(aCx, JSVAL_TO_OBJECT(aKeyPath))) {
1695 :
1696 4 : JSObject* obj = JSVAL_TO_OBJECT(aKeyPath);
1697 :
1698 : uint32_t length;
1699 4 : if (!JS_GetArrayLength(aCx, obj, &length)) {
1700 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1701 : }
1702 :
1703 4 : if (!length) {
1704 2 : return NS_ERROR_DOM_SYNTAX_ERR;
1705 : }
1706 :
1707 2 : keyPathArray.SetCapacity(length);
1708 :
1709 4 : for (uint32_t index = 0; index < length; index++) {
1710 : jsval val;
1711 : JSString* jsstr;
1712 4 : nsDependentJSString str;
1713 4 : if (!JS_GetElement(aCx, obj, index, &val) ||
1714 : !(jsstr = JS_ValueToString(aCx, val)) ||
1715 2 : !str.init(aCx, jsstr)) {
1716 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1717 : }
1718 :
1719 2 : if (!IsValidKeyPath(aCx, str)) {
1720 0 : return NS_ERROR_DOM_SYNTAX_ERR;
1721 : }
1722 :
1723 4 : keyPathArray.AppendElement(str);
1724 : }
1725 :
1726 2 : NS_ASSERTION(!keyPathArray.IsEmpty(), "This shouldn't have happened!");
1727 : }
1728 : else {
1729 : JSString* jsstr;
1730 176 : nsDependentJSString str;
1731 176 : if (!(jsstr = JS_ValueToString(aCx, aKeyPath)) ||
1732 88 : !str.init(aCx, jsstr)) {
1733 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1734 : }
1735 :
1736 88 : if (!IsValidKeyPath(aCx, str)) {
1737 0 : return NS_ERROR_DOM_SYNTAX_ERR;
1738 : }
1739 :
1740 176 : keyPath = str;
1741 : }
1742 :
1743 : // Check name and current mode
1744 90 : IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction();
1745 :
1746 268 : if (!transaction ||
1747 89 : transaction != mTransaction ||
1748 89 : mTransaction->GetMode() != IDBTransaction::VERSION_CHANGE) {
1749 1 : return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
1750 : }
1751 :
1752 89 : bool found = false;
1753 89 : PRUint32 indexCount = mInfo->indexes.Length();
1754 163 : for (PRUint32 index = 0; index < indexCount; index++) {
1755 74 : if (mInfo->indexes[index].name == aName) {
1756 0 : found = true;
1757 0 : break;
1758 : }
1759 : }
1760 :
1761 89 : if (found) {
1762 0 : return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
1763 : }
1764 :
1765 89 : NS_ASSERTION(mTransaction->IsOpen(), "Impossible!");
1766 :
1767 178 : mozilla::dom::IDBIndexParameters params;
1768 :
1769 : // Get optional arguments.
1770 89 : if (!JSVAL_IS_VOID(aOptions) && !JSVAL_IS_NULL(aOptions)) {
1771 79 : nsresult rv = params.Init(aCx, &aOptions);
1772 79 : NS_ENSURE_SUCCESS(rv, rv);
1773 : }
1774 :
1775 87 : if (params.multiEntry && !keyPathArray.IsEmpty()) {
1776 2 : return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1777 : }
1778 :
1779 85 : DatabaseInfo* databaseInfo = mTransaction->DBInfo();
1780 :
1781 85 : IndexInfo* indexInfo = mInfo->indexes.AppendElement();
1782 85 : indexInfo->id = databaseInfo->nextIndexId++;
1783 85 : indexInfo->name = aName;
1784 85 : indexInfo->keyPath = keyPath;
1785 85 : indexInfo->keyPathArray.SwapElements(keyPathArray);
1786 85 : indexInfo->unique = params.unique;
1787 85 : indexInfo->multiEntry = params.multiEntry;
1788 :
1789 : // Don't leave this in the list if we fail below!
1790 170 : AutoRemoveIndex autoRemove(mInfo, aName);
1791 :
1792 : #ifdef DEBUG
1793 159 : for (PRUint32 index = 0; index < mCreatedIndexes.Length(); index++) {
1794 74 : if (mCreatedIndexes[index]->Name() == aName) {
1795 0 : NS_ERROR("Already created this one!");
1796 : }
1797 : }
1798 : #endif
1799 :
1800 170 : nsRefPtr<IDBIndex> index(IDBIndex::Create(this, indexInfo));
1801 :
1802 85 : if (!mCreatedIndexes.AppendElement(index)) {
1803 0 : NS_WARNING("Out of memory!");
1804 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1805 : }
1806 :
1807 : nsRefPtr<CreateIndexHelper> helper =
1808 255 : new CreateIndexHelper(mTransaction, index);
1809 :
1810 85 : nsresult rv = helper->DispatchToTransactionPool();
1811 85 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1812 :
1813 85 : autoRemove.forget();
1814 :
1815 85 : index.forget(_retval);
1816 85 : return NS_OK;
1817 : }
1818 :
1819 : NS_IMETHODIMP
1820 94 : IDBObjectStore::Index(const nsAString& aName,
1821 : nsIIDBIndex** _retval)
1822 : {
1823 94 : NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
1824 :
1825 94 : if (!mTransaction->IsOpen()) {
1826 1 : return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
1827 : }
1828 :
1829 93 : IndexInfo* indexInfo = nsnull;
1830 93 : PRUint32 indexCount = mInfo->indexes.Length();
1831 135 : for (PRUint32 index = 0; index < indexCount; index++) {
1832 134 : if (mInfo->indexes[index].name == aName) {
1833 92 : indexInfo = &(mInfo->indexes[index]);
1834 92 : break;
1835 : }
1836 : }
1837 :
1838 93 : if (!indexInfo) {
1839 1 : return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR;
1840 : }
1841 :
1842 184 : nsRefPtr<IDBIndex> retval;
1843 108 : for (PRUint32 i = 0; i < mCreatedIndexes.Length(); i++) {
1844 64 : nsRefPtr<IDBIndex>& index = mCreatedIndexes[i];
1845 64 : if (index->Name() == aName) {
1846 48 : retval = index;
1847 48 : break;
1848 : }
1849 : }
1850 :
1851 92 : if (!retval) {
1852 44 : retval = IDBIndex::Create(this, indexInfo);
1853 44 : NS_ENSURE_TRUE(retval, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1854 :
1855 44 : if (!mCreatedIndexes.AppendElement(retval)) {
1856 0 : NS_WARNING("Out of memory!");
1857 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1858 : }
1859 : }
1860 :
1861 92 : retval.forget(_retval);
1862 92 : return NS_OK;
1863 : }
1864 :
1865 : NS_IMETHODIMP
1866 25 : IDBObjectStore::DeleteIndex(const nsAString& aName)
1867 : {
1868 25 : NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
1869 :
1870 25 : IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction();
1871 :
1872 73 : if (!transaction ||
1873 24 : transaction != mTransaction ||
1874 24 : mTransaction->GetMode() != IDBTransaction::VERSION_CHANGE) {
1875 1 : return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
1876 : }
1877 :
1878 24 : NS_ASSERTION(mTransaction->IsOpen(), "Impossible!");
1879 :
1880 24 : PRUint32 index = 0;
1881 44 : for (; index < mInfo->indexes.Length(); index++) {
1882 44 : if (mInfo->indexes[index].name == aName) {
1883 24 : break;
1884 : }
1885 : }
1886 :
1887 24 : if (index == mInfo->indexes.Length()) {
1888 0 : return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR;
1889 : }
1890 :
1891 : nsRefPtr<DeleteIndexHelper> helper =
1892 72 : new DeleteIndexHelper(mTransaction, aName, this);
1893 :
1894 24 : nsresult rv = helper->DispatchToTransactionPool();
1895 24 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1896 :
1897 24 : mInfo->indexes.RemoveElementAt(index);
1898 :
1899 44 : for (PRUint32 i = 0; i < mCreatedIndexes.Length(); i++) {
1900 44 : if (mCreatedIndexes[i]->Name() == aName) {
1901 24 : mCreatedIndexes.RemoveElementAt(i);
1902 24 : break;
1903 : }
1904 : }
1905 :
1906 24 : return NS_OK;
1907 : }
1908 :
1909 : NS_IMETHODIMP
1910 32 : IDBObjectStore::Count(const jsval& aKey,
1911 : JSContext* aCx,
1912 : PRUint8 aOptionalArgCount,
1913 : nsIIDBRequest** _retval)
1914 : {
1915 32 : if (!mTransaction->IsOpen()) {
1916 0 : return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
1917 : }
1918 :
1919 : nsresult rv;
1920 :
1921 64 : nsRefPtr<IDBKeyRange> keyRange;
1922 32 : if (aOptionalArgCount) {
1923 16 : rv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
1924 16 : NS_ENSURE_SUCCESS(rv, rv);
1925 : }
1926 :
1927 64 : nsRefPtr<IDBRequest> request = GenerateRequest(this);
1928 32 : NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1929 :
1930 : nsRefPtr<CountHelper> helper =
1931 96 : new CountHelper(mTransaction, request, this, keyRange);
1932 32 : rv = helper->DispatchToTransactionPool();
1933 32 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1934 :
1935 32 : request.forget(_retval);
1936 32 : return NS_OK;
1937 : }
1938 :
1939 : inline nsresult
1940 0 : CopyData(nsIInputStream* aStream, quota_FILE* aFile)
1941 : {
1942 0 : do {
1943 : char copyBuffer[FILE_COPY_BUFFER_SIZE];
1944 :
1945 : PRUint32 numRead;
1946 0 : nsresult rv = aStream->Read(copyBuffer, FILE_COPY_BUFFER_SIZE, &numRead);
1947 0 : NS_ENSURE_SUCCESS(rv, rv);
1948 :
1949 0 : if (numRead <= 0) {
1950 : break;
1951 : }
1952 :
1953 0 : size_t numWrite = sqlite3_quota_fwrite(copyBuffer, 1, numRead, aFile);
1954 0 : NS_ENSURE_TRUE(numWrite == numRead, NS_ERROR_FAILURE);
1955 : } while (true);
1956 :
1957 : // Flush and sync
1958 0 : NS_ENSURE_TRUE(sqlite3_quota_fflush(aFile, 1) == 0,
1959 : NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1960 :
1961 0 : return NS_OK;
1962 : }
1963 :
1964 : nsresult
1965 1837 : AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
1966 : {
1967 1837 : NS_PRECONDITION(aConnection, "Passed a null connection!");
1968 :
1969 : nsresult rv;
1970 1837 : bool keyUnset = mKey.IsUnset();
1971 1837 : PRInt64 osid = mObjectStore->Id();
1972 1837 : const nsString& keyPath = mObjectStore->KeyPath();
1973 :
1974 : // The "|| keyUnset" here is mostly a debugging tool. If a key isn't
1975 : // specified we should never have a collision and so it shouldn't matter
1976 : // if we allow overwrite or not. By not allowing overwrite we raise
1977 : // detectable errors rather than corrupting data
1978 1837 : nsCOMPtr<mozIStorageStatement> stmt = !mOverwrite || keyUnset ?
1979 : mTransaction->GetCachedStatement(
1980 : "INSERT INTO object_data (object_store_id, key_value, data, file_ids) "
1981 1787 : "VALUES (:osid, :key_value, :data, :file_ids)") :
1982 : mTransaction->GetCachedStatement(
1983 : "INSERT OR REPLACE INTO object_data (object_store_id, key_value, data, file_ids) "
1984 5461 : "VALUES (:osid, :key_value, :data, :file_ids)");
1985 1837 : NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1986 :
1987 3674 : mozStorageStatementScoper scoper(stmt);
1988 :
1989 1837 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), osid);
1990 1837 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1991 :
1992 1837 : NS_ASSERTION(!keyUnset || mObjectStore->IsAutoIncrement(),
1993 : "Should have key unless autoincrement");
1994 :
1995 1837 : PRInt64 autoIncrementNum = 0;
1996 :
1997 1837 : if (mObjectStore->IsAutoIncrement()) {
1998 1385 : if (keyUnset) {
1999 1307 : autoIncrementNum = mObjectStore->Info()->nextAutoIncrementId;
2000 1307 : if (autoIncrementNum > (1LL << 53)) {
2001 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
2002 : }
2003 1307 : mKey.SetFromInteger(autoIncrementNum);
2004 : }
2005 156 : else if (mKey.IsFloat() &&
2006 78 : mKey.ToFloat() >= mObjectStore->Info()->nextAutoIncrementId) {
2007 55 : autoIncrementNum = floor(mKey.ToFloat());
2008 : }
2009 :
2010 1385 : if (keyUnset && !keyPath.IsEmpty()) {
2011 : // Special case where someone put an object into an autoIncrement'ing
2012 : // objectStore with no key in its keyPath set. We needed to figure out
2013 : // which row id we would get above before we could set that properly.
2014 :
2015 : // This is a duplicate of the js engine's byte munging here
2016 : union {
2017 : double d;
2018 : PRUint64 u;
2019 : } pun;
2020 :
2021 16 : pun.d = SwapBytes(static_cast<PRUint64>(autoIncrementNum));
2022 :
2023 16 : JSAutoStructuredCloneBuffer& buffer = mCloneWriteInfo.mCloneBuffer;
2024 16 : PRUint64 offsetToKeyProp = mCloneWriteInfo.mOffsetToKeyProp;
2025 :
2026 16 : memcpy((char*)buffer.data() + offsetToKeyProp, &pun.u, sizeof(PRUint64));
2027 : }
2028 : }
2029 :
2030 1837 : mKey.BindToStatement(stmt, NS_LITERAL_CSTRING("key_value"));
2031 :
2032 : // Compress the bytes before adding into the database.
2033 : const char* uncompressed =
2034 1837 : reinterpret_cast<const char*>(mCloneWriteInfo.mCloneBuffer.data());
2035 1837 : size_t uncompressedLength = mCloneWriteInfo.mCloneBuffer.nbytes();
2036 :
2037 1837 : size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength);
2038 : // This will hold our compressed data until the end of the method. The
2039 : // BindBlobByName function will copy it.
2040 5511 : nsAutoArrayPtr<char> compressed(new char[compressedLength]);
2041 :
2042 : snappy::RawCompress(uncompressed, uncompressedLength, compressed.get(),
2043 1837 : &compressedLength);
2044 :
2045 1837 : const PRUint8* dataBuffer = reinterpret_cast<const PRUint8*>(compressed.get());
2046 1837 : size_t dataBufferLength = compressedLength;
2047 :
2048 3674 : rv = stmt->BindBlobByName(NS_LITERAL_CSTRING("data"), dataBuffer,
2049 1837 : dataBufferLength);
2050 1837 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2051 :
2052 : // Handle blobs
2053 3674 : nsRefPtr<FileManager> fileManager = mDatabase->Manager();
2054 3674 : nsCOMPtr<nsIFile> directory = fileManager->GetDirectory();
2055 1837 : NS_ENSURE_TRUE(directory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2056 :
2057 3674 : nsAutoString fileIds;
2058 :
2059 1837 : for (PRUint32 index = 0; index < mCloneWriteInfo.mBlobs.Length(); index++) {
2060 0 : nsCOMPtr<nsIDOMBlob>& domBlob = mCloneWriteInfo.mBlobs[index];
2061 :
2062 0 : PRInt64 id = -1;
2063 :
2064 : // Check if it is a blob created from this db or the blob was already
2065 : // stored in this db
2066 0 : nsRefPtr<FileInfo> fileInfo = domBlob->GetFileInfo(fileManager);
2067 0 : if (fileInfo) {
2068 0 : id = fileInfo->Id();
2069 : }
2070 :
2071 0 : if (id == -1) {
2072 0 : fileInfo = fileManager->GetNewFileInfo();
2073 0 : NS_ENSURE_TRUE(fileInfo, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2074 :
2075 0 : id = fileInfo->Id();
2076 :
2077 0 : mTransaction->OnNewFileInfo(fileInfo);
2078 :
2079 : // Copy it
2080 0 : nsCOMPtr<nsIInputStream> inputStream;
2081 0 : rv = domBlob->GetInternalStream(getter_AddRefs(inputStream));
2082 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2083 :
2084 0 : nsCOMPtr<nsIFile> nativeFile = fileManager->GetFileForId(directory, id);
2085 0 : NS_ENSURE_TRUE(nativeFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2086 :
2087 0 : nsString nativeFilePath;
2088 0 : rv = nativeFile->GetPath(nativeFilePath);
2089 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2090 :
2091 : quota_FILE* file =
2092 0 : sqlite3_quota_fopen(NS_ConvertUTF16toUTF8(nativeFilePath).get(), "wb");
2093 0 : NS_ENSURE_TRUE(file, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2094 :
2095 0 : rv = CopyData(inputStream, file);
2096 :
2097 0 : NS_ENSURE_TRUE(sqlite3_quota_fclose(file) == 0,
2098 : NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2099 :
2100 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2101 :
2102 0 : domBlob->AddFileInfo(fileInfo);
2103 : }
2104 :
2105 0 : if (index) {
2106 0 : fileIds.Append(NS_LITERAL_STRING(" "));
2107 : }
2108 0 : fileIds.AppendInt(id);
2109 : }
2110 :
2111 1837 : if (fileIds.IsEmpty()) {
2112 1837 : rv = stmt->BindNullByName(NS_LITERAL_CSTRING("file_ids"));
2113 : }
2114 : else {
2115 0 : rv = stmt->BindStringByName(NS_LITERAL_CSTRING("file_ids"), fileIds);
2116 : }
2117 1837 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2118 :
2119 1837 : rv = stmt->Execute();
2120 1837 : if (rv == NS_ERROR_STORAGE_CONSTRAINT) {
2121 12 : NS_ASSERTION(!keyUnset, "Generated key had a collision!?");
2122 12 : return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
2123 : }
2124 1825 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2125 :
2126 : PRInt64 objectDataId;
2127 1825 : rv = aConnection->GetLastInsertRowID(&objectDataId);
2128 1825 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2129 :
2130 : // Update our indexes if needed.
2131 1825 : if (mOverwrite || !mIndexUpdateInfo.IsEmpty()) {
2132 : rv = IDBObjectStore::UpdateIndexes(mTransaction, osid, mKey, mOverwrite,
2133 172 : objectDataId, mIndexUpdateInfo);
2134 172 : if (rv == NS_ERROR_STORAGE_CONSTRAINT) {
2135 7 : return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
2136 : }
2137 165 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2138 : }
2139 :
2140 1818 : if (autoIncrementNum) {
2141 1361 : mObjectStore->Info()->nextAutoIncrementId = autoIncrementNum + 1;
2142 : }
2143 :
2144 1818 : return NS_OK;
2145 : }
2146 :
2147 : nsresult
2148 1813 : AddHelper::GetSuccessResult(JSContext* aCx,
2149 : jsval* aVal)
2150 : {
2151 1813 : NS_ASSERTION(!mKey.IsUnset(), "Badness!");
2152 :
2153 1813 : mCloneWriteInfo.mCloneBuffer.clear();
2154 :
2155 1813 : return mKey.ToJSVal(aCx, aVal);
2156 : }
2157 :
2158 : nsresult
2159 173 : GetHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
2160 : {
2161 173 : NS_ASSERTION(mKeyRange, "Must have a key range here!");
2162 :
2163 346 : nsCString keyRangeClause;
2164 173 : mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("key_value"), keyRangeClause);
2165 :
2166 173 : NS_ASSERTION(!keyRangeClause.IsEmpty(), "Huh?!");
2167 :
2168 173 : nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM object_data "
2169 : "WHERE object_store_id = :osid") +
2170 519 : keyRangeClause + NS_LITERAL_CSTRING(" LIMIT 1");
2171 :
2172 346 : nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
2173 173 : NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2174 :
2175 346 : mozStorageStatementScoper scoper(stmt);
2176 :
2177 173 : nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), mObjectStore->Id());
2178 173 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2179 :
2180 173 : rv = mKeyRange->BindToStatement(stmt);
2181 173 : NS_ENSURE_SUCCESS(rv, rv);
2182 :
2183 : bool hasResult;
2184 173 : rv = stmt->ExecuteStep(&hasResult);
2185 173 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2186 :
2187 173 : if (hasResult) {
2188 : rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1,
2189 162 : mDatabase->Manager(), mCloneReadInfo);
2190 162 : NS_ENSURE_SUCCESS(rv, rv);
2191 : }
2192 :
2193 173 : return NS_OK;
2194 : }
2195 :
2196 : nsresult
2197 167 : GetHelper::GetSuccessResult(JSContext* aCx,
2198 : jsval* aVal)
2199 : {
2200 167 : bool result = IDBObjectStore::DeserializeValue(aCx, mCloneReadInfo, aVal);
2201 :
2202 167 : mCloneReadInfo.mCloneBuffer.clear();
2203 :
2204 167 : NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
2205 167 : return NS_OK;
2206 : }
2207 :
2208 : nsresult
2209 26 : DeleteHelper::DoDatabaseWork(mozIStorageConnection* /*aConnection */)
2210 : {
2211 26 : NS_ASSERTION(mKeyRange, "Must have a key range here!");
2212 :
2213 52 : nsCString keyRangeClause;
2214 26 : mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("key_value"), keyRangeClause);
2215 :
2216 26 : NS_ASSERTION(!keyRangeClause.IsEmpty(), "Huh?!");
2217 :
2218 26 : nsCString query = NS_LITERAL_CSTRING("DELETE FROM object_data "
2219 : "WHERE object_store_id = :osid") +
2220 52 : keyRangeClause;
2221 :
2222 52 : nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
2223 26 : NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2224 :
2225 52 : mozStorageStatementScoper scoper(stmt);
2226 :
2227 52 : nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"),
2228 26 : mObjectStore->Id());
2229 26 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2230 :
2231 26 : rv = mKeyRange->BindToStatement(stmt);
2232 26 : NS_ENSURE_SUCCESS(rv, rv);
2233 :
2234 26 : rv = stmt->Execute();
2235 26 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2236 :
2237 26 : return NS_OK;
2238 : }
2239 :
2240 : nsresult
2241 26 : DeleteHelper::GetSuccessResult(JSContext* aCx,
2242 : jsval* aVal)
2243 : {
2244 26 : *aVal = JSVAL_VOID;
2245 26 : return NS_OK;
2246 : }
2247 :
2248 : nsresult
2249 10 : ClearHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
2250 : {
2251 10 : NS_PRECONDITION(aConnection, "Passed a null connection!");
2252 :
2253 : nsCOMPtr<mozIStorageStatement> stmt =
2254 : mTransaction->GetCachedStatement(
2255 10 : NS_LITERAL_CSTRING("DELETE FROM object_data "
2256 20 : "WHERE object_store_id = :osid"));
2257 10 : NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2258 :
2259 20 : mozStorageStatementScoper scoper(stmt);
2260 :
2261 20 : nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"),
2262 10 : mObjectStore->Id());
2263 10 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2264 :
2265 10 : rv = stmt->Execute();
2266 10 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2267 :
2268 10 : return NS_OK;
2269 : }
2270 :
2271 : nsresult
2272 47 : OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
2273 : {
2274 94 : NS_NAMED_LITERAL_CSTRING(keyValue, "key_value");
2275 :
2276 94 : nsCString keyRangeClause;
2277 47 : if (mKeyRange) {
2278 10 : mKeyRange->GetBindingClause(keyValue, keyRangeClause);
2279 : }
2280 :
2281 94 : nsCAutoString directionClause;
2282 47 : switch (mDirection) {
2283 : case IDBCursor::NEXT:
2284 : case IDBCursor::NEXT_UNIQUE:
2285 44 : directionClause.AssignLiteral(" ORDER BY key_value ASC");
2286 44 : break;
2287 :
2288 : case IDBCursor::PREV:
2289 : case IDBCursor::PREV_UNIQUE:
2290 3 : directionClause.AssignLiteral(" ORDER BY key_value DESC");
2291 3 : break;
2292 :
2293 : default:
2294 0 : NS_NOTREACHED("Unknown direction type!");
2295 : }
2296 :
2297 47 : nsCString firstQuery = NS_LITERAL_CSTRING("SELECT key_value, data, file_ids "
2298 : "FROM object_data "
2299 : "WHERE object_store_id = :id") +
2300 47 : keyRangeClause + directionClause +
2301 188 : NS_LITERAL_CSTRING(" LIMIT 1");
2302 :
2303 : nsCOMPtr<mozIStorageStatement> stmt =
2304 94 : mTransaction->GetCachedStatement(firstQuery);
2305 47 : NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2306 :
2307 94 : mozStorageStatementScoper scoper(stmt);
2308 :
2309 94 : nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"),
2310 47 : mObjectStore->Id());
2311 47 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2312 :
2313 47 : if (mKeyRange) {
2314 10 : rv = mKeyRange->BindToStatement(stmt);
2315 10 : NS_ENSURE_SUCCESS(rv, rv);
2316 : }
2317 :
2318 : bool hasResult;
2319 47 : rv = stmt->ExecuteStep(&hasResult);
2320 47 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2321 :
2322 47 : if (!hasResult) {
2323 9 : mKey.Unset();
2324 9 : return NS_OK;
2325 : }
2326 :
2327 38 : rv = mKey.SetFromStatement(stmt, 0);
2328 38 : NS_ENSURE_SUCCESS(rv, rv);
2329 :
2330 : rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 1, 2,
2331 38 : mDatabase->Manager(), mCloneReadInfo);
2332 38 : NS_ENSURE_SUCCESS(rv, rv);
2333 :
2334 : // Now we need to make the query to get the next match.
2335 38 : keyRangeClause.Truncate();
2336 76 : nsCAutoString continueToKeyRangeClause;
2337 :
2338 76 : NS_NAMED_LITERAL_CSTRING(currentKey, "current_key");
2339 76 : NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key");
2340 :
2341 38 : switch (mDirection) {
2342 : case IDBCursor::NEXT:
2343 : case IDBCursor::NEXT_UNIQUE:
2344 : AppendConditionClause(keyValue, currentKey, false, false,
2345 35 : keyRangeClause);
2346 : AppendConditionClause(keyValue, currentKey, false, true,
2347 35 : continueToKeyRangeClause);
2348 35 : if (mKeyRange && !mKeyRange->Upper().IsUnset()) {
2349 : AppendConditionClause(keyValue, rangeKey, true,
2350 5 : !mKeyRange->IsUpperOpen(), keyRangeClause);
2351 : AppendConditionClause(keyValue, rangeKey, true,
2352 5 : !mKeyRange->IsUpperOpen(),
2353 5 : continueToKeyRangeClause);
2354 5 : mRangeKey = mKeyRange->Upper();
2355 : }
2356 35 : break;
2357 :
2358 : case IDBCursor::PREV:
2359 : case IDBCursor::PREV_UNIQUE:
2360 3 : AppendConditionClause(keyValue, currentKey, true, false, keyRangeClause);
2361 : AppendConditionClause(keyValue, currentKey, true, true,
2362 3 : continueToKeyRangeClause);
2363 3 : if (mKeyRange && !mKeyRange->Lower().IsUnset()) {
2364 : AppendConditionClause(keyValue, rangeKey, false,
2365 0 : !mKeyRange->IsLowerOpen(), keyRangeClause);
2366 : AppendConditionClause(keyValue, rangeKey, false,
2367 0 : !mKeyRange->IsLowerOpen(),
2368 0 : continueToKeyRangeClause);
2369 0 : mRangeKey = mKeyRange->Lower();
2370 : }
2371 3 : break;
2372 :
2373 : default:
2374 0 : NS_NOTREACHED("Unknown direction type!");
2375 : }
2376 :
2377 76 : NS_NAMED_LITERAL_CSTRING(queryStart, "SELECT key_value, data, file_ids "
2378 : "FROM object_data "
2379 : "WHERE object_store_id = :id");
2380 :
2381 38 : mContinueQuery = queryStart + keyRangeClause + directionClause +
2382 76 : NS_LITERAL_CSTRING(" LIMIT ");
2383 :
2384 38 : mContinueToQuery = queryStart + continueToKeyRangeClause + directionClause +
2385 76 : NS_LITERAL_CSTRING(" LIMIT ");
2386 :
2387 38 : return NS_OK;
2388 : }
2389 :
2390 : nsresult
2391 47 : OpenCursorHelper::GetSuccessResult(JSContext* aCx,
2392 : jsval* aVal)
2393 : {
2394 47 : if (mKey.IsUnset()) {
2395 9 : *aVal = JSVAL_VOID;
2396 9 : return NS_OK;
2397 : }
2398 :
2399 : nsRefPtr<IDBCursor> cursor =
2400 : IDBCursor::Create(mRequest, mTransaction, mObjectStore, mDirection,
2401 : mRangeKey, mContinueQuery, mContinueToQuery, mKey,
2402 76 : mCloneReadInfo);
2403 38 : NS_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2404 :
2405 38 : NS_ASSERTION(!mCloneReadInfo.mCloneBuffer.data(), "Should have swapped!");
2406 :
2407 38 : return WrapNative(aCx, cursor, aVal);
2408 : }
2409 :
2410 : class ThreadLocalJSRuntime
2411 : {
2412 : JSRuntime* mRuntime;
2413 : JSContext* mContext;
2414 : JSObject* mGlobal;
2415 :
2416 : static JSClass sGlobalClass;
2417 : static const unsigned sRuntimeHeapSize = 256 * 1024; // should be enough for anyone
2418 :
2419 8 : ThreadLocalJSRuntime()
2420 8 : : mRuntime(NULL), mContext(NULL), mGlobal(NULL)
2421 : {
2422 8 : MOZ_COUNT_CTOR(ThreadLocalJSRuntime);
2423 8 : }
2424 :
2425 8 : nsresult Init()
2426 : {
2427 8 : mRuntime = JS_NewRuntime(sRuntimeHeapSize);
2428 8 : NS_ENSURE_TRUE(mRuntime, NS_ERROR_OUT_OF_MEMORY);
2429 :
2430 8 : mContext = JS_NewContext(mRuntime, 0);
2431 8 : NS_ENSURE_TRUE(mContext, NS_ERROR_OUT_OF_MEMORY);
2432 :
2433 16 : JSAutoRequest ar(mContext);
2434 :
2435 8 : mGlobal = JS_NewCompartmentAndGlobalObject(mContext, &sGlobalClass, NULL);
2436 8 : NS_ENSURE_TRUE(mGlobal, NS_ERROR_OUT_OF_MEMORY);
2437 :
2438 8 : JS_SetGlobalObject(mContext, mGlobal);
2439 8 : return NS_OK;
2440 : }
2441 :
2442 : public:
2443 8 : static ThreadLocalJSRuntime *Create()
2444 : {
2445 8 : ThreadLocalJSRuntime *entry = new ThreadLocalJSRuntime();
2446 8 : NS_ENSURE_TRUE(entry, nsnull);
2447 :
2448 8 : if (NS_FAILED(entry->Init())) {
2449 0 : delete entry;
2450 0 : return nsnull;
2451 : }
2452 :
2453 8 : return entry;
2454 : }
2455 :
2456 171 : JSContext *Context() const
2457 : {
2458 171 : return mContext;
2459 : }
2460 :
2461 8 : ~ThreadLocalJSRuntime()
2462 : {
2463 8 : MOZ_COUNT_DTOR(ThreadLocalJSRuntime);
2464 :
2465 8 : if (mContext) {
2466 8 : JS_DestroyContext(mContext);
2467 : }
2468 :
2469 8 : if (mRuntime) {
2470 8 : JS_DestroyRuntime(mRuntime);
2471 : }
2472 8 : }
2473 : };
2474 :
2475 : JSClass ThreadLocalJSRuntime::sGlobalClass = {
2476 : "IndexedDBTransactionThreadGlobal",
2477 : JSCLASS_GLOBAL_FLAGS,
2478 : JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
2479 : JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
2480 : };
2481 :
2482 85 : CreateIndexHelper::CreateIndexHelper(IDBTransaction* aTransaction,
2483 : IDBIndex* aIndex)
2484 85 : : AsyncConnectionHelper(aTransaction, nsnull), mIndex(aIndex)
2485 : {
2486 85 : if (sTLSIndex == BAD_TLS_INDEX) {
2487 22 : PR_NewThreadPrivateIndex(&sTLSIndex, DestroyTLSEntry);
2488 : }
2489 85 : }
2490 :
2491 : void
2492 8 : CreateIndexHelper::DestroyTLSEntry(void* aPtr)
2493 : {
2494 8 : delete reinterpret_cast<ThreadLocalJSRuntime *>(aPtr);
2495 8 : }
2496 :
2497 : nsresult
2498 85 : CreateIndexHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
2499 : {
2500 : // Insert the data into the database.
2501 : nsCOMPtr<mozIStorageStatement> stmt =
2502 : mTransaction->GetCachedStatement(
2503 : "INSERT INTO object_store_index (id, name, key_path, unique_index, "
2504 : "multientry, object_store_id) "
2505 : "VALUES (:id, :name, :key_path, :unique, :multientry, :osid)"
2506 170 : );
2507 85 : NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2508 :
2509 170 : mozStorageStatementScoper scoper(stmt);
2510 :
2511 170 : nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"),
2512 85 : mIndex->Id());
2513 85 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2514 :
2515 85 : rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mIndex->Name());
2516 85 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2517 :
2518 85 : if (mIndex->UsesKeyPathArray()) {
2519 : // We use a comma in the beginning to indicate that it's an array of
2520 : // key paths. This is to be able to tell a string-keypath from an
2521 : // array-keypath which contains only one item.
2522 : // It also makes serializing easier :-)
2523 0 : nsAutoString keyPath;
2524 0 : const nsTArray<nsString>& keyPaths = mIndex->KeyPathArray();
2525 0 : for (PRUint32 i = 0; i < keyPaths.Length(); ++i) {
2526 0 : keyPath.Append(NS_LITERAL_STRING(",") + keyPaths[i]);
2527 : }
2528 0 : rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key_path"),
2529 0 : keyPath);
2530 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2531 : }
2532 : else {
2533 170 : rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key_path"),
2534 85 : mIndex->KeyPath());
2535 85 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2536 : }
2537 :
2538 170 : rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("unique"),
2539 85 : mIndex->IsUnique() ? 1 : 0);
2540 85 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2541 :
2542 170 : rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("multientry"),
2543 85 : mIndex->IsMultiEntry() ? 1 : 0);
2544 85 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2545 :
2546 170 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"),
2547 85 : mIndex->ObjectStore()->Id());
2548 85 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2549 :
2550 85 : if (NS_FAILED(stmt->Execute())) {
2551 0 : return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
2552 : }
2553 :
2554 : #ifdef DEBUG
2555 : {
2556 : PRInt64 id;
2557 85 : aConnection->GetLastInsertRowID(&id);
2558 85 : NS_ASSERTION(mIndex->Id() == id, "Bad index id!");
2559 : }
2560 : #endif
2561 :
2562 : // Now we need to populate the index with data from the object store.
2563 85 : rv = InsertDataFromObjectStore(aConnection);
2564 85 : if (NS_FAILED(rv)) {
2565 0 : return rv;
2566 : }
2567 :
2568 85 : return NS_OK;
2569 : }
2570 :
2571 : nsresult
2572 85 : CreateIndexHelper::InsertDataFromObjectStore(mozIStorageConnection* aConnection)
2573 : {
2574 : nsCOMPtr<mozIStorageStatement> stmt =
2575 : mTransaction->GetCachedStatement(
2576 85 : NS_LITERAL_CSTRING("SELECT id, data, file_ids, key_value FROM object_data "
2577 170 : "WHERE object_store_id = :osid"));
2578 85 : NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2579 :
2580 170 : mozStorageStatementScoper scoper(stmt);
2581 :
2582 170 : nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"),
2583 85 : mIndex->ObjectStore()->Id());
2584 85 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2585 :
2586 85 : NS_ENSURE_TRUE(sTLSIndex != BAD_TLS_INDEX, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2587 :
2588 : bool hasResult;
2589 85 : rv = stmt->ExecuteStep(&hasResult);
2590 85 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2591 85 : if (!hasResult) {
2592 : // Bail early if we have no data to avoid creating the below runtime
2593 51 : return NS_OK;
2594 : }
2595 :
2596 : ThreadLocalJSRuntime* tlsEntry =
2597 34 : reinterpret_cast<ThreadLocalJSRuntime*>(PR_GetThreadPrivate(sTLSIndex));
2598 :
2599 34 : if (!tlsEntry) {
2600 8 : tlsEntry = ThreadLocalJSRuntime::Create();
2601 8 : NS_ENSURE_TRUE(tlsEntry, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2602 :
2603 8 : PR_SetThreadPrivate(sTLSIndex, tlsEntry);
2604 : }
2605 :
2606 34 : JSContext* cx = tlsEntry->Context();
2607 68 : JSAutoRequest ar(cx);
2608 :
2609 274 : do {
2610 274 : StructuredCloneReadInfo cloneReadInfo;
2611 : rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 1, 2,
2612 137 : mDatabase->Manager(), cloneReadInfo);
2613 137 : NS_ENSURE_SUCCESS(rv, rv);
2614 :
2615 137 : JSAutoStructuredCloneBuffer& buffer = cloneReadInfo.mCloneBuffer;
2616 :
2617 : JSStructuredCloneCallbacks callbacks = {
2618 : IDBObjectStore::StructuredCloneReadCallback,
2619 : nsnull,
2620 : nsnull
2621 137 : };
2622 :
2623 : jsval clone;
2624 137 : if (!buffer.read(cx, &clone, &callbacks, &cloneReadInfo)) {
2625 0 : NS_WARNING("Failed to deserialize structured clone data!");
2626 0 : return NS_ERROR_DOM_DATA_CLONE_ERR;
2627 : }
2628 :
2629 274 : nsTArray<IndexUpdateInfo> updateInfo;
2630 : rv = IDBObjectStore::AppendIndexUpdateInfo(mIndex->Id(),
2631 137 : mIndex->KeyPath(),
2632 137 : mIndex->KeyPathArray(),
2633 137 : mIndex->IsUnique(),
2634 137 : mIndex->IsMultiEntry(),
2635 : tlsEntry->Context(),
2636 548 : clone, updateInfo);
2637 137 : NS_ENSURE_SUCCESS(rv, rv);
2638 :
2639 137 : PRInt64 objectDataID = stmt->AsInt64(0);
2640 :
2641 274 : Key key;
2642 137 : rv = key.SetFromStatement(stmt, 3);
2643 137 : NS_ENSURE_SUCCESS(rv, rv);
2644 :
2645 : rv = IDBObjectStore::UpdateIndexes(mTransaction, mIndex->Id(),
2646 137 : key, false, objectDataID, updateInfo);
2647 137 : NS_ENSURE_SUCCESS(rv, rv);
2648 :
2649 274 : } while (NS_SUCCEEDED(rv = stmt->ExecuteStep(&hasResult)) && hasResult);
2650 34 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2651 :
2652 34 : return NS_OK;
2653 : }
2654 :
2655 : nsresult
2656 24 : DeleteIndexHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
2657 : {
2658 24 : NS_PRECONDITION(!NS_IsMainThread(), "Wrong thread!");
2659 :
2660 : nsCOMPtr<mozIStorageStatement> stmt =
2661 : mTransaction->GetCachedStatement(
2662 : "DELETE FROM object_store_index "
2663 : "WHERE name = :name "
2664 48 : );
2665 24 : NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2666 :
2667 48 : mozStorageStatementScoper scoper(stmt);
2668 :
2669 24 : nsresult rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mName);
2670 24 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2671 :
2672 24 : if (NS_FAILED(stmt->Execute())) {
2673 0 : return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR;
2674 : }
2675 :
2676 24 : return NS_OK;
2677 : }
2678 :
2679 : nsresult
2680 17 : GetAllHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
2681 : {
2682 34 : NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key");
2683 34 : NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key");
2684 :
2685 34 : nsCAutoString keyRangeClause;
2686 17 : if (mKeyRange) {
2687 11 : if (!mKeyRange->Lower().IsUnset()) {
2688 11 : keyRangeClause = NS_LITERAL_CSTRING(" AND key_value");
2689 11 : if (mKeyRange->IsLowerOpen()) {
2690 1 : keyRangeClause.AppendLiteral(" > :");
2691 : }
2692 : else {
2693 10 : keyRangeClause.AppendLiteral(" >= :");
2694 : }
2695 11 : keyRangeClause.Append(lowerKeyName);
2696 : }
2697 :
2698 11 : if (!mKeyRange->Upper().IsUnset()) {
2699 10 : keyRangeClause += NS_LITERAL_CSTRING(" AND key_value");
2700 10 : if (mKeyRange->IsUpperOpen()) {
2701 1 : keyRangeClause.AppendLiteral(" < :");
2702 : }
2703 : else {
2704 9 : keyRangeClause.AppendLiteral(" <= :");
2705 : }
2706 10 : keyRangeClause.Append(upperKeyName);
2707 : }
2708 : }
2709 :
2710 34 : nsCAutoString limitClause;
2711 17 : if (mLimit != PR_UINT32_MAX) {
2712 3 : limitClause.AssignLiteral(" LIMIT ");
2713 3 : limitClause.AppendInt(mLimit);
2714 : }
2715 :
2716 17 : nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM object_data "
2717 : "WHERE object_store_id = :osid") +
2718 17 : keyRangeClause +
2719 51 : NS_LITERAL_CSTRING(" ORDER BY key_value ASC") +
2720 34 : limitClause;
2721 :
2722 17 : mCloneReadInfos.SetCapacity(50);
2723 :
2724 34 : nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
2725 17 : NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2726 :
2727 34 : mozStorageStatementScoper scoper(stmt);
2728 :
2729 34 : nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"),
2730 17 : mObjectStore->Id());
2731 17 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2732 :
2733 17 : if (mKeyRange) {
2734 11 : if (!mKeyRange->Lower().IsUnset()) {
2735 11 : rv = mKeyRange->Lower().BindToStatement(stmt, lowerKeyName);
2736 11 : NS_ENSURE_SUCCESS(rv, rv);
2737 : }
2738 11 : if (!mKeyRange->Upper().IsUnset()) {
2739 10 : rv = mKeyRange->Upper().BindToStatement(stmt, upperKeyName);
2740 10 : NS_ENSURE_SUCCESS(rv, rv);
2741 : }
2742 : }
2743 :
2744 : bool hasResult;
2745 133 : while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
2746 99 : if (mCloneReadInfos.Capacity() == mCloneReadInfos.Length()) {
2747 0 : if (!mCloneReadInfos.SetCapacity(mCloneReadInfos.Capacity() * 2)) {
2748 0 : NS_ERROR("Out of memory!");
2749 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
2750 : }
2751 : }
2752 :
2753 99 : StructuredCloneReadInfo* readInfo = mCloneReadInfos.AppendElement();
2754 99 : NS_ASSERTION(readInfo, "Shouldn't fail if SetCapacity succeeded!");
2755 :
2756 : rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1,
2757 99 : mDatabase->Manager(), *readInfo);
2758 99 : NS_ENSURE_SUCCESS(rv, rv);
2759 : }
2760 17 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2761 :
2762 17 : return NS_OK;
2763 : }
2764 :
2765 : nsresult
2766 17 : GetAllHelper::GetSuccessResult(JSContext* aCx,
2767 : jsval* aVal)
2768 : {
2769 17 : NS_ASSERTION(mCloneReadInfos.Length() <= mLimit, "Too many results!");
2770 :
2771 17 : nsresult rv = ConvertCloneReadInfosToArray(aCx, mCloneReadInfos, aVal);
2772 :
2773 17 : for (PRUint32 index = 0; index < mCloneReadInfos.Length(); index++) {
2774 0 : mCloneReadInfos[index].mCloneBuffer.clear();
2775 : }
2776 :
2777 17 : NS_ENSURE_SUCCESS(rv, rv);
2778 17 : return NS_OK;
2779 : }
2780 :
2781 : nsresult
2782 32 : CountHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
2783 : {
2784 64 : NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key");
2785 64 : NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key");
2786 :
2787 64 : nsCAutoString keyRangeClause;
2788 32 : if (mKeyRange) {
2789 15 : if (!mKeyRange->Lower().IsUnset()) {
2790 12 : keyRangeClause = NS_LITERAL_CSTRING(" AND key_value");
2791 12 : if (mKeyRange->IsLowerOpen()) {
2792 5 : keyRangeClause.AppendLiteral(" > :");
2793 : }
2794 : else {
2795 7 : keyRangeClause.AppendLiteral(" >= :");
2796 : }
2797 12 : keyRangeClause.Append(lowerKeyName);
2798 : }
2799 :
2800 15 : if (!mKeyRange->Upper().IsUnset()) {
2801 12 : keyRangeClause += NS_LITERAL_CSTRING(" AND key_value");
2802 12 : if (mKeyRange->IsUpperOpen()) {
2803 5 : keyRangeClause.AppendLiteral(" < :");
2804 : }
2805 : else {
2806 7 : keyRangeClause.AppendLiteral(" <= :");
2807 : }
2808 12 : keyRangeClause.Append(upperKeyName);
2809 : }
2810 : }
2811 :
2812 32 : nsCString query = NS_LITERAL_CSTRING("SELECT count(*) FROM object_data "
2813 : "WHERE object_store_id = :osid") +
2814 64 : keyRangeClause;
2815 :
2816 64 : nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
2817 32 : NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2818 :
2819 64 : mozStorageStatementScoper scoper(stmt);
2820 :
2821 64 : nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"),
2822 32 : mObjectStore->Id());
2823 32 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2824 :
2825 32 : if (mKeyRange) {
2826 15 : if (!mKeyRange->Lower().IsUnset()) {
2827 12 : rv = mKeyRange->Lower().BindToStatement(stmt, lowerKeyName);
2828 12 : NS_ENSURE_SUCCESS(rv, rv);
2829 : }
2830 15 : if (!mKeyRange->Upper().IsUnset()) {
2831 12 : rv = mKeyRange->Upper().BindToStatement(stmt, upperKeyName);
2832 12 : NS_ENSURE_SUCCESS(rv, rv);
2833 : }
2834 : }
2835 :
2836 : bool hasResult;
2837 32 : rv = stmt->ExecuteStep(&hasResult);
2838 32 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2839 32 : NS_ENSURE_TRUE(hasResult, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2840 :
2841 32 : mCount = stmt->AsInt64(0);
2842 32 : return NS_OK;
2843 : }
2844 :
2845 : nsresult
2846 32 : CountHelper::GetSuccessResult(JSContext* aCx,
2847 : jsval* aVal)
2848 : {
2849 32 : return JS_NewNumberValue(aCx, static_cast<double>(mCount), aVal);
2850 4392 : }
|