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 "IDBDatabase.h"
41 :
42 : #include "mozilla/Mutex.h"
43 : #include "mozilla/storage.h"
44 : #include "nsDOMClassInfo.h"
45 : #include "nsDOMLists.h"
46 : #include "nsEventDispatcher.h"
47 : #include "nsJSUtils.h"
48 : #include "nsProxyRelease.h"
49 : #include "nsThreadUtils.h"
50 :
51 : #include "AsyncConnectionHelper.h"
52 : #include "CheckQuotaHelper.h"
53 : #include "DatabaseInfo.h"
54 : #include "IDBEvents.h"
55 : #include "IDBIndex.h"
56 : #include "IDBObjectStore.h"
57 : #include "IDBTransaction.h"
58 : #include "IDBFactory.h"
59 : #include "IndexedDatabaseManager.h"
60 : #include "TransactionThreadPool.h"
61 : #include "DictionaryHelpers.h"
62 : #include "nsDOMEventTargetHelper.h"
63 :
64 : USING_INDEXEDDB_NAMESPACE
65 :
66 : namespace {
67 :
68 : class CreateObjectStoreHelper : public AsyncConnectionHelper
69 804 : {
70 : public:
71 201 : CreateObjectStoreHelper(IDBTransaction* aTransaction,
72 : IDBObjectStore* aObjectStore)
73 201 : : AsyncConnectionHelper(aTransaction, nsnull), mObjectStore(aObjectStore)
74 201 : { }
75 :
76 : nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
77 :
78 200 : nsresult OnSuccess()
79 : {
80 200 : return NS_OK;
81 : }
82 :
83 1 : void OnError()
84 : {
85 1 : NS_ASSERTION(mTransaction->IsAborted(), "How else can this fail?!");
86 1 : }
87 :
88 201 : void ReleaseMainThreadObjects()
89 : {
90 201 : mObjectStore = nsnull;
91 201 : AsyncConnectionHelper::ReleaseMainThreadObjects();
92 201 : }
93 :
94 : private:
95 : nsRefPtr<IDBObjectStore> mObjectStore;
96 : };
97 :
98 : class DeleteObjectStoreHelper : public AsyncConnectionHelper
99 492 : {
100 : public:
101 123 : DeleteObjectStoreHelper(IDBTransaction* aTransaction,
102 : PRInt64 aObjectStoreId)
103 123 : : AsyncConnectionHelper(aTransaction, nsnull), mObjectStoreId(aObjectStoreId)
104 123 : { }
105 :
106 : nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
107 :
108 123 : nsresult OnSuccess()
109 : {
110 123 : return NS_OK;
111 : }
112 :
113 0 : void OnError()
114 : {
115 0 : NS_ASSERTION(mTransaction->IsAborted(), "How else can this fail?!");
116 0 : }
117 :
118 : private:
119 : // In-params.
120 : PRInt64 mObjectStoreId;
121 : };
122 :
123 : NS_STACK_CLASS
124 : class AutoRemoveObjectStore
125 : {
126 : public:
127 201 : AutoRemoveObjectStore(DatabaseInfo* aInfo, const nsAString& aName)
128 201 : : mInfo(aInfo), mName(aName)
129 201 : { }
130 :
131 201 : ~AutoRemoveObjectStore()
132 201 : {
133 201 : if (mInfo) {
134 0 : mInfo->RemoveObjectStore(mName);
135 : }
136 201 : }
137 :
138 201 : void forget()
139 : {
140 201 : mInfo = nsnull;
141 201 : }
142 :
143 : private:
144 : DatabaseInfo* mInfo;
145 : nsString mName;
146 : };
147 :
148 : } // anonymous namespace
149 :
150 : // static
151 : already_AddRefed<IDBDatabase>
152 75 : IDBDatabase::Create(IDBWrapperCache* aOwnerCache,
153 : already_AddRefed<DatabaseInfo> aDatabaseInfo,
154 : const nsACString& aASCIIOrigin,
155 : FileManager* aFileManager)
156 : {
157 75 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
158 75 : NS_ASSERTION(!aASCIIOrigin.IsEmpty(), "Empty origin!");
159 :
160 150 : nsRefPtr<DatabaseInfo> databaseInfo(aDatabaseInfo);
161 75 : NS_ASSERTION(databaseInfo, "Null pointer!");
162 :
163 150 : nsRefPtr<IDBDatabase> db(new IDBDatabase());
164 :
165 75 : db->BindToOwner(aOwnerCache);
166 75 : if (!db->SetScriptOwner(aOwnerCache->GetScriptOwner())) {
167 0 : return nsnull;
168 : }
169 :
170 75 : db->mDatabaseId = databaseInfo->id;
171 75 : db->mName = databaseInfo->name;
172 75 : db->mFilePath = databaseInfo->filePath;
173 75 : databaseInfo.swap(db->mDatabaseInfo);
174 75 : db->mASCIIOrigin = aASCIIOrigin;
175 75 : db->mFileManager = aFileManager;
176 :
177 75 : IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
178 75 : NS_ASSERTION(mgr, "This should never be null!");
179 :
180 75 : if (!mgr->RegisterDatabase(db)) {
181 : // Either out of memory or shutting down.
182 0 : return nsnull;
183 : }
184 :
185 75 : return db.forget();
186 : }
187 :
188 75 : IDBDatabase::IDBDatabase()
189 : : mDatabaseId(0),
190 : mInvalidated(0),
191 : mRegistered(false),
192 : mClosed(false),
193 75 : mRunningVersionChange(false)
194 : {
195 75 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
196 75 : }
197 :
198 225 : IDBDatabase::~IDBDatabase()
199 : {
200 75 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
201 :
202 75 : if (mRegistered) {
203 75 : CloseInternal(true);
204 :
205 75 : IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
206 75 : if (mgr) {
207 12 : mgr->UnregisterDatabase(this);
208 : }
209 : }
210 :
211 75 : nsContentUtils::ReleaseWrapper(static_cast<nsIDOMEventTarget*>(this), this);
212 300 : }
213 :
214 : void
215 0 : IDBDatabase::Invalidate()
216 : {
217 0 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
218 :
219 : // Make sure we're closed too.
220 0 : Close();
221 :
222 : // When the IndexedDatabaseManager needs to invalidate databases, all it has
223 : // is an origin, so we call back into the manager to cancel any prompts for
224 : // our owner.
225 0 : IndexedDatabaseManager::CancelPromptsForWindow(GetOwner());
226 :
227 0 : mInvalidated = true;
228 0 : }
229 :
230 : bool
231 12101 : IDBDatabase::IsInvalidated()
232 : {
233 12101 : return !!mInvalidated;
234 : }
235 :
236 : void
237 162 : IDBDatabase::CloseInternal(bool aIsDead)
238 : {
239 162 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
240 :
241 162 : if (!mClosed) {
242 : // If we're getting called from Unlink, avoid cloning the DatabaseInfo.
243 : {
244 150 : nsRefPtr<DatabaseInfo> previousInfo;
245 75 : mDatabaseInfo.swap(previousInfo);
246 :
247 75 : if (!aIsDead) {
248 52 : nsRefPtr<DatabaseInfo> clonedInfo = previousInfo->Clone();
249 :
250 26 : clonedInfo.swap(mDatabaseInfo);
251 : }
252 : }
253 :
254 75 : IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
255 75 : if (mgr) {
256 31 : mgr->OnDatabaseClosed(this);
257 : }
258 75 : mClosed = true;
259 : }
260 162 : }
261 :
262 : bool
263 114 : IDBDatabase::IsClosed()
264 : {
265 114 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
266 114 : return mClosed;
267 : }
268 :
269 : void
270 71 : IDBDatabase::EnterSetVersionTransaction()
271 : {
272 71 : NS_ASSERTION(!mRunningVersionChange, "How did that happen?");
273 71 : mRunningVersionChange = true;
274 71 : }
275 :
276 : void
277 71 : IDBDatabase::ExitSetVersionTransaction()
278 : {
279 71 : NS_ASSERTION(mRunningVersionChange, "How did that happen?");
280 71 : mRunningVersionChange = false;
281 71 : }
282 :
283 : void
284 61 : IDBDatabase::OnUnlink()
285 : {
286 61 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
287 61 : NS_ASSERTION(!GetOwner() && !GetScriptOwner(),
288 : "Should have been cleared already!");
289 :
290 : // We've been unlinked, at the very least we should be able to prevent further
291 : // transactions from starting and unblock any other SetVersion callers.
292 61 : CloseInternal(true);
293 :
294 : // No reason for the IndexedDatabaseManager to track us any longer.
295 61 : IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
296 61 : if (mgr) {
297 0 : mgr->UnregisterDatabase(this);
298 :
299 : // Don't try to unregister again in the destructor.
300 0 : mRegistered = false;
301 : }
302 61 : }
303 :
304 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(IDBDatabase)
305 :
306 62 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBDatabase, IDBWrapperCache)
307 62 : NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(abort)
308 62 : NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(error)
309 62 : NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(versionchange)
310 62 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
311 :
312 61 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBDatabase, IDBWrapperCache)
313 61 : NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(abort)
314 61 : NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(error)
315 61 : NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(versionchange)
316 :
317 : // Do some cleanup.
318 61 : tmp->OnUnlink();
319 61 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
320 :
321 18608 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBDatabase)
322 9329 : NS_INTERFACE_MAP_ENTRY(nsIIDBDatabase)
323 8210 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBDatabase)
324 8135 : NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache)
325 :
326 16404 : NS_IMPL_ADDREF_INHERITED(IDBDatabase, IDBWrapperCache)
327 16404 : NS_IMPL_RELEASE_INHERITED(IDBDatabase, IDBWrapperCache)
328 :
329 : DOMCI_DATA(IDBDatabase, IDBDatabase)
330 :
331 1 : NS_IMPL_EVENT_HANDLER(IDBDatabase, abort);
332 13 : NS_IMPL_EVENT_HANDLER(IDBDatabase, error);
333 1 : NS_IMPL_EVENT_HANDLER(IDBDatabase, versionchange);
334 :
335 : NS_IMETHODIMP
336 9 : IDBDatabase::GetName(nsAString& aName)
337 : {
338 9 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
339 9 : aName.Assign(mName);
340 9 : return NS_OK;
341 : }
342 :
343 : NS_IMETHODIMP
344 24 : IDBDatabase::GetVersion(PRUint64* aVersion)
345 : {
346 24 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
347 :
348 24 : DatabaseInfo* info = Info();
349 24 : *aVersion = info->version;
350 :
351 24 : return NS_OK;
352 : }
353 :
354 : NS_IMETHODIMP
355 219 : IDBDatabase::GetObjectStoreNames(nsIDOMDOMStringList** aObjectStores)
356 : {
357 219 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
358 :
359 219 : DatabaseInfo* info = Info();
360 :
361 438 : nsAutoTArray<nsString, 10> objectStoreNames;
362 219 : if (!info->GetObjectStoreNames(objectStoreNames)) {
363 0 : NS_WARNING("Couldn't get names!");
364 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
365 : }
366 :
367 438 : nsRefPtr<nsDOMStringList> list(new nsDOMStringList());
368 219 : PRUint32 count = objectStoreNames.Length();
369 904 : for (PRUint32 index = 0; index < count; index++) {
370 685 : NS_ENSURE_TRUE(list->Add(objectStoreNames[index]),
371 : NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
372 : }
373 :
374 219 : list.forget(aObjectStores);
375 219 : return NS_OK;
376 : }
377 :
378 : NS_IMETHODIMP
379 202 : IDBDatabase::CreateObjectStore(const nsAString& aName,
380 : const jsval& aOptions,
381 : JSContext* aCx,
382 : nsIIDBObjectStore** _retval)
383 : {
384 202 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
385 :
386 202 : IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction();
387 :
388 404 : if (!transaction ||
389 202 : transaction->GetMode() != IDBTransaction::VERSION_CHANGE) {
390 0 : return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
391 : }
392 :
393 202 : DatabaseInfo* databaseInfo = transaction->DBInfo();
394 :
395 404 : mozilla::dom::IDBObjectStoreParameters params;
396 404 : nsString keyPath;
397 202 : keyPath.SetIsVoid(true);
398 404 : nsTArray<nsString> keyPathArray;
399 :
400 202 : if (!JSVAL_IS_VOID(aOptions) && !JSVAL_IS_NULL(aOptions)) {
401 185 : nsresult rv = params.Init(aCx, &aOptions);
402 185 : NS_ENSURE_SUCCESS(rv, rv);
403 :
404 : // Get keyPath
405 184 : jsval val = params.keyPath;
406 184 : if (!JSVAL_IS_VOID(val) && !JSVAL_IS_NULL(val)) {
407 125 : if (!JSVAL_IS_PRIMITIVE(val) &&
408 0 : JS_IsArrayObject(aCx, JSVAL_TO_OBJECT(val))) {
409 :
410 0 : JSObject* obj = JSVAL_TO_OBJECT(val);
411 :
412 : uint32_t length;
413 0 : if (!JS_GetArrayLength(aCx, obj, &length)) {
414 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
415 : }
416 :
417 0 : if (!length) {
418 0 : return NS_ERROR_DOM_SYNTAX_ERR;
419 : }
420 :
421 0 : keyPathArray.SetCapacity(length);
422 :
423 0 : for (uint32_t index = 0; index < length; index++) {
424 : jsval val;
425 : JSString* jsstr;
426 0 : nsDependentJSString str;
427 0 : if (!JS_GetElement(aCx, obj, index, &val) ||
428 : !(jsstr = JS_ValueToString(aCx, val)) ||
429 0 : !str.init(aCx, jsstr)) {
430 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
431 : }
432 :
433 0 : if (!IDBObjectStore::IsValidKeyPath(aCx, str)) {
434 0 : return NS_ERROR_DOM_SYNTAX_ERR;
435 : }
436 :
437 0 : keyPathArray.AppendElement(str);
438 : }
439 :
440 0 : NS_ASSERTION(!keyPathArray.IsEmpty(), "This shouldn't have happened!");
441 : }
442 : else {
443 : JSString* jsstr;
444 250 : nsDependentJSString str;
445 250 : if (!(jsstr = JS_ValueToString(aCx, val)) ||
446 125 : !str.init(aCx, jsstr)) {
447 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
448 : }
449 :
450 125 : if (!IDBObjectStore::IsValidKeyPath(aCx, str)) {
451 0 : return NS_ERROR_DOM_SYNTAX_ERR;
452 : }
453 :
454 250 : keyPath = str;
455 : }
456 : }
457 : }
458 :
459 201 : if (databaseInfo->ContainsStoreName(aName)) {
460 0 : return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
461 : }
462 :
463 428 : if (params.autoIncrement &&
464 227 : ((!keyPath.IsVoid() && keyPath.IsEmpty()) || !keyPathArray.IsEmpty())) {
465 0 : return NS_ERROR_DOM_INVALID_ACCESS_ERR;
466 : }
467 :
468 402 : nsRefPtr<ObjectStoreInfo> newInfo(new ObjectStoreInfo());
469 :
470 201 : newInfo->name = aName;
471 201 : newInfo->id = databaseInfo->nextObjectStoreId++;
472 201 : newInfo->keyPath = keyPath;
473 201 : newInfo->keyPathArray = keyPathArray;
474 201 : newInfo->nextAutoIncrementId = params.autoIncrement ? 1 : 0;
475 201 : newInfo->comittedAutoIncrementId = newInfo->nextAutoIncrementId;
476 :
477 201 : if (!databaseInfo->PutObjectStore(newInfo)) {
478 0 : NS_WARNING("Put failed!");
479 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
480 : }
481 :
482 : // Don't leave this in the hash if we fail below!
483 402 : AutoRemoveObjectStore autoRemove(databaseInfo, aName);
484 :
485 : nsRefPtr<IDBObjectStore> objectStore =
486 402 : transaction->GetOrCreateObjectStore(aName, newInfo);
487 201 : NS_ENSURE_TRUE(objectStore, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
488 :
489 : nsRefPtr<CreateObjectStoreHelper> helper =
490 603 : new CreateObjectStoreHelper(transaction, objectStore);
491 :
492 201 : nsresult rv = helper->DispatchToTransactionPool();
493 201 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
494 :
495 201 : autoRemove.forget();
496 :
497 201 : objectStore.forget(_retval);
498 201 : return NS_OK;
499 : }
500 :
501 : NS_IMETHODIMP
502 123 : IDBDatabase::DeleteObjectStore(const nsAString& aName)
503 : {
504 123 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
505 :
506 123 : IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction();
507 :
508 246 : if (!transaction ||
509 123 : transaction->GetMode() != IDBTransaction::VERSION_CHANGE) {
510 0 : return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
511 : }
512 :
513 123 : DatabaseInfo* info = transaction->DBInfo();
514 123 : ObjectStoreInfo* objectStoreInfo = info->GetObjectStore(aName);
515 123 : if (!objectStoreInfo) {
516 0 : return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR;
517 : }
518 :
519 : nsRefPtr<DeleteObjectStoreHelper> helper =
520 246 : new DeleteObjectStoreHelper(transaction, objectStoreInfo->id);
521 123 : nsresult rv = helper->DispatchToTransactionPool();
522 123 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
523 :
524 123 : transaction->RemoveObjectStore(aName);
525 :
526 123 : return NS_OK;
527 : }
528 :
529 : NS_IMETHODIMP
530 471 : IDBDatabase::Transaction(const jsval& aStoreNames,
531 : const nsAString& aMode,
532 : JSContext* aCx,
533 : PRUint8 aOptionalArgCount,
534 : nsIIDBTransaction** _retval)
535 : {
536 471 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
537 :
538 471 : if (IndexedDatabaseManager::IsShuttingDown()) {
539 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
540 : }
541 :
542 471 : if (mClosed) {
543 0 : return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
544 : }
545 :
546 471 : if (mRunningVersionChange) {
547 2 : return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
548 : }
549 :
550 469 : IDBTransaction::Mode transactionMode = IDBTransaction::READ_ONLY;
551 469 : if (aOptionalArgCount) {
552 298 : if (aMode.EqualsLiteral("readwrite")) {
553 297 : transactionMode = IDBTransaction::READ_WRITE;
554 : }
555 1 : else if (!aMode.EqualsLiteral("readonly")) {
556 0 : return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
557 : }
558 : }
559 :
560 : nsresult rv;
561 938 : nsTArray<nsString> storesToOpen;
562 :
563 469 : if (!JSVAL_IS_PRIMITIVE(aStoreNames)) {
564 253 : JSObject* obj = JSVAL_TO_OBJECT(aStoreNames);
565 :
566 : // See if this is a JS array.
567 253 : if (JS_IsArrayObject(aCx, obj)) {
568 : uint32_t length;
569 251 : if (!JS_GetArrayLength(aCx, obj, &length)) {
570 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
571 : }
572 :
573 251 : if (!length) {
574 0 : return NS_ERROR_DOM_INVALID_ACCESS_ERR;
575 : }
576 :
577 251 : storesToOpen.SetCapacity(length);
578 :
579 602 : for (uint32_t index = 0; index < length; index++) {
580 : jsval val;
581 : JSString* jsstr;
582 702 : nsDependentJSString str;
583 702 : if (!JS_GetElement(aCx, obj, index, &val) ||
584 : !(jsstr = JS_ValueToString(aCx, val)) ||
585 351 : !str.init(aCx, jsstr)) {
586 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
587 : }
588 :
589 702 : storesToOpen.AppendElement(str);
590 : }
591 :
592 251 : NS_ASSERTION(!storesToOpen.IsEmpty(),
593 : "Must have something here or else code below will "
594 : "misbehave!");
595 : }
596 : else {
597 : // Perhaps some kind of wrapped object?
598 2 : nsIXPConnect* xpc = nsContentUtils::XPConnect();
599 2 : NS_ASSERTION(xpc, "This should never be null!");
600 :
601 4 : nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
602 2 : rv = xpc->GetWrappedNativeOfJSObject(aCx, obj, getter_AddRefs(wrapper));
603 2 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
604 :
605 2 : if (wrapper) {
606 2 : nsISupports* wrappedObject = wrapper->Native();
607 2 : NS_ENSURE_TRUE(wrappedObject, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
608 :
609 : // We only accept DOMStringList.
610 4 : nsCOMPtr<nsIDOMDOMStringList> list = do_QueryInterface(wrappedObject);
611 2 : if (list) {
612 : PRUint32 length;
613 2 : rv = list->GetLength(&length);
614 2 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
615 :
616 2 : if (!length) {
617 0 : return NS_ERROR_DOM_INVALID_ACCESS_ERR;
618 : }
619 :
620 2 : storesToOpen.SetCapacity(length);
621 :
622 4 : for (PRUint32 index = 0; index < length; index++) {
623 2 : nsString* item = storesToOpen.AppendElement();
624 2 : NS_ASSERTION(item, "This should never fail!");
625 :
626 2 : rv = list->Item(index, *item);
627 2 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
628 : }
629 :
630 2 : NS_ASSERTION(!storesToOpen.IsEmpty(),
631 : "Must have something here or else code below will "
632 : "misbehave!");
633 : }
634 : }
635 : }
636 : }
637 :
638 : // If our list is empty here then the argument must have been an object that
639 : // we don't support or a primitive. Either way we convert to a string.
640 469 : if (storesToOpen.IsEmpty()) {
641 : JSString* jsstr;
642 432 : nsDependentJSString str;
643 432 : if (!(jsstr = JS_ValueToString(aCx, aStoreNames)) ||
644 216 : !str.init(aCx, jsstr)) {
645 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
646 : }
647 :
648 432 : storesToOpen.AppendElement(str);
649 : }
650 :
651 : // Now check to make sure the object store names we collected actually exist.
652 469 : DatabaseInfo* info = Info();
653 1038 : for (PRUint32 index = 0; index < storesToOpen.Length(); index++) {
654 569 : if (!info->ContainsStoreName(storesToOpen[index])) {
655 0 : return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR;
656 : }
657 : }
658 :
659 : nsRefPtr<IDBTransaction> transaction =
660 938 : IDBTransaction::Create(this, storesToOpen, transactionMode, false);
661 469 : NS_ENSURE_TRUE(transaction, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
662 :
663 469 : transaction.forget(_retval);
664 469 : return NS_OK;
665 : }
666 :
667 : NS_IMETHODIMP
668 26 : IDBDatabase::Close()
669 : {
670 26 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
671 :
672 26 : CloseInternal(false);
673 :
674 26 : NS_ASSERTION(mClosed, "Should have set the closed flag!");
675 26 : return NS_OK;
676 : }
677 :
678 : nsresult
679 44 : IDBDatabase::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
680 : {
681 44 : NS_ENSURE_TRUE(aVisitor.mDOMEvent, NS_ERROR_UNEXPECTED);
682 :
683 44 : if (!GetOwner()) {
684 44 : return NS_OK;
685 : }
686 :
687 0 : if (aVisitor.mEventStatus != nsEventStatus_eConsumeNoDefault) {
688 0 : nsString type;
689 0 : nsresult rv = aVisitor.mDOMEvent->GetType(type);
690 0 : NS_ENSURE_SUCCESS(rv, rv);
691 :
692 0 : if (type.EqualsLiteral(ERROR_EVT_STR)) {
693 : nsRefPtr<nsDOMEvent> duplicateEvent =
694 0 : CreateGenericEvent(type, eDoesNotBubble, eNotCancelable);
695 0 : NS_ENSURE_STATE(duplicateEvent);
696 :
697 0 : nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(GetOwner()));
698 0 : NS_ASSERTION(target, "How can this happen?!");
699 :
700 : bool dummy;
701 0 : rv = target->DispatchEvent(duplicateEvent, &dummy);
702 0 : NS_ENSURE_SUCCESS(rv, rv);
703 : }
704 : }
705 :
706 0 : return NS_OK;
707 : }
708 :
709 : nsresult
710 201 : CreateObjectStoreHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
711 : {
712 : nsCOMPtr<mozIStorageStatement> stmt =
713 201 : mTransaction->GetCachedStatement(NS_LITERAL_CSTRING(
714 : "INSERT INTO object_store (id, auto_increment, name, key_path) "
715 : "VALUES (:id, :auto_increment, :name, :key_path)"
716 402 : ));
717 201 : NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
718 :
719 402 : mozStorageStatementScoper scoper(stmt);
720 :
721 402 : nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"),
722 201 : mObjectStore->Id());
723 201 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
724 :
725 402 : rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("auto_increment"),
726 201 : mObjectStore->IsAutoIncrement() ? 1 : 0);
727 201 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
728 :
729 201 : rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mObjectStore->Name());
730 201 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
731 :
732 201 : if (mObjectStore->UsesKeyPathArray()) {
733 : // We use a comma in the beginning to indicate that it's an array of
734 : // key paths. This is to be able to tell a string-keypath from an
735 : // array-keypath which contains only one item.
736 : // It also makes serializing easier :-)
737 0 : nsAutoString keyPath;
738 0 : const nsTArray<nsString>& keyPaths = mObjectStore->KeyPathArray();
739 0 : for (PRUint32 i = 0; i < keyPaths.Length(); ++i) {
740 0 : keyPath.Append(NS_LITERAL_STRING(",") + keyPaths[i]);
741 : }
742 0 : rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key_path"),
743 0 : keyPath);
744 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
745 : }
746 201 : else if (mObjectStore->HasKeyPath()) {
747 250 : rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key_path"),
748 125 : mObjectStore->KeyPath());
749 125 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
750 : }
751 : else {
752 76 : rv = stmt->BindNullByName(NS_LITERAL_CSTRING("key_path"));
753 76 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
754 : }
755 :
756 :
757 201 : rv = stmt->Execute();
758 201 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
759 :
760 201 : return NS_OK;
761 : }
762 :
763 : nsresult
764 123 : DeleteObjectStoreHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
765 : {
766 : nsCOMPtr<mozIStorageStatement> stmt =
767 123 : mTransaction->GetCachedStatement(NS_LITERAL_CSTRING(
768 : "DELETE FROM object_store "
769 : "WHERE id = :id "
770 246 : ));
771 123 : NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
772 :
773 246 : mozStorageStatementScoper scoper(stmt);
774 :
775 123 : nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mObjectStoreId);
776 123 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
777 :
778 123 : rv = stmt->Execute();
779 123 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
780 :
781 123 : return NS_OK;
782 4392 : }
|