|
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 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "base/basictypes.h" |
|
8 |
|
9 #include "IDBDatabase.h" |
|
10 |
|
11 #include "mozilla/EventDispatcher.h" |
|
12 #include "mozilla/Mutex.h" |
|
13 #include "mozilla/storage.h" |
|
14 #include "mozilla/dom/ContentParent.h" |
|
15 #include "mozilla/dom/DOMStringList.h" |
|
16 #include "mozilla/dom/DOMStringListBinding.h" |
|
17 #include "mozilla/dom/quota/Client.h" |
|
18 #include "mozilla/dom/quota/QuotaManager.h" |
|
19 #include "nsJSUtils.h" |
|
20 #include "nsProxyRelease.h" |
|
21 #include "nsThreadUtils.h" |
|
22 |
|
23 #include "AsyncConnectionHelper.h" |
|
24 #include "DatabaseInfo.h" |
|
25 #include "IDBEvents.h" |
|
26 #include "IDBFactory.h" |
|
27 #include "IDBFileHandle.h" |
|
28 #include "IDBIndex.h" |
|
29 #include "IDBObjectStore.h" |
|
30 #include "IDBTransaction.h" |
|
31 #include "IDBFactory.h" |
|
32 #include "ProfilerHelpers.h" |
|
33 #include "ReportInternalError.h" |
|
34 #include "TransactionThreadPool.h" |
|
35 |
|
36 #include "ipc/IndexedDBChild.h" |
|
37 #include "ipc/IndexedDBParent.h" |
|
38 |
|
39 #include "mozilla/dom/IDBDatabaseBinding.h" |
|
40 |
|
41 USING_INDEXEDDB_NAMESPACE |
|
42 using mozilla::dom::ContentParent; |
|
43 using mozilla::dom::quota::AssertIsOnIOThread; |
|
44 using mozilla::dom::quota::Client; |
|
45 using mozilla::dom::quota::QuotaManager; |
|
46 using mozilla::ErrorResult; |
|
47 using namespace mozilla; |
|
48 using namespace mozilla::dom; |
|
49 |
|
50 namespace { |
|
51 |
|
52 class NoRequestDatabaseHelper : public AsyncConnectionHelper |
|
53 { |
|
54 public: |
|
55 NoRequestDatabaseHelper(IDBTransaction* aTransaction) |
|
56 : AsyncConnectionHelper(aTransaction, nullptr) |
|
57 { |
|
58 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); |
|
59 NS_ASSERTION(aTransaction, "Null transaction!"); |
|
60 } |
|
61 |
|
62 virtual ChildProcessSendResult |
|
63 SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; |
|
64 |
|
65 virtual nsresult |
|
66 UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) |
|
67 MOZ_OVERRIDE; |
|
68 |
|
69 virtual nsresult OnSuccess() MOZ_OVERRIDE; |
|
70 |
|
71 virtual void OnError() MOZ_OVERRIDE; |
|
72 }; |
|
73 |
|
74 class CreateObjectStoreHelper : public NoRequestDatabaseHelper |
|
75 { |
|
76 public: |
|
77 CreateObjectStoreHelper(IDBTransaction* aTransaction, |
|
78 IDBObjectStore* aObjectStore) |
|
79 : NoRequestDatabaseHelper(aTransaction), mObjectStore(aObjectStore) |
|
80 { } |
|
81 |
|
82 virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) |
|
83 MOZ_OVERRIDE; |
|
84 |
|
85 virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; |
|
86 |
|
87 private: |
|
88 nsRefPtr<IDBObjectStore> mObjectStore; |
|
89 }; |
|
90 |
|
91 class DeleteObjectStoreHelper : public NoRequestDatabaseHelper |
|
92 { |
|
93 public: |
|
94 DeleteObjectStoreHelper(IDBTransaction* aTransaction, |
|
95 int64_t aObjectStoreId) |
|
96 : NoRequestDatabaseHelper(aTransaction), mObjectStoreId(aObjectStoreId) |
|
97 { } |
|
98 |
|
99 virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) |
|
100 MOZ_OVERRIDE; |
|
101 |
|
102 private: |
|
103 // In-params. |
|
104 int64_t mObjectStoreId; |
|
105 }; |
|
106 |
|
107 class CreateFileHelper : public AsyncConnectionHelper |
|
108 { |
|
109 public: |
|
110 CreateFileHelper(IDBDatabase* aDatabase, |
|
111 IDBRequest* aRequest, |
|
112 const nsAString& aName, |
|
113 const nsAString& aType) |
|
114 : AsyncConnectionHelper(aDatabase, aRequest), |
|
115 mName(aName), mType(aType) |
|
116 { } |
|
117 |
|
118 ~CreateFileHelper() |
|
119 { } |
|
120 |
|
121 nsresult DoDatabaseWork(mozIStorageConnection* aConnection); |
|
122 nsresult GetSuccessResult(JSContext* aCx, |
|
123 JS::MutableHandle<JS::Value> aVal); |
|
124 void ReleaseMainThreadObjects() |
|
125 { |
|
126 mFileInfo = nullptr; |
|
127 AsyncConnectionHelper::ReleaseMainThreadObjects(); |
|
128 } |
|
129 |
|
130 virtual ChildProcessSendResult SendResponseToChildProcess( |
|
131 nsresult aResultCode) |
|
132 MOZ_OVERRIDE |
|
133 { |
|
134 return Success_NotSent; |
|
135 } |
|
136 |
|
137 virtual nsresult UnpackResponseFromParentProcess( |
|
138 const ResponseValue& aResponseValue) |
|
139 MOZ_OVERRIDE |
|
140 { |
|
141 MOZ_CRASH("Should never get here!"); |
|
142 } |
|
143 |
|
144 private: |
|
145 // In-params. |
|
146 nsString mName; |
|
147 nsString mType; |
|
148 |
|
149 // Out-params. |
|
150 nsRefPtr<FileInfo> mFileInfo; |
|
151 }; |
|
152 |
|
153 class MOZ_STACK_CLASS AutoRemoveObjectStore |
|
154 { |
|
155 public: |
|
156 AutoRemoveObjectStore(DatabaseInfo* aInfo, const nsAString& aName) |
|
157 : mInfo(aInfo), mName(aName) |
|
158 { } |
|
159 |
|
160 ~AutoRemoveObjectStore() |
|
161 { |
|
162 if (mInfo) { |
|
163 mInfo->RemoveObjectStore(mName); |
|
164 } |
|
165 } |
|
166 |
|
167 void forget() |
|
168 { |
|
169 mInfo = nullptr; |
|
170 } |
|
171 |
|
172 private: |
|
173 DatabaseInfo* mInfo; |
|
174 nsString mName; |
|
175 }; |
|
176 |
|
177 } // anonymous namespace |
|
178 |
|
179 // static |
|
180 already_AddRefed<IDBDatabase> |
|
181 IDBDatabase::Create(IDBWrapperCache* aOwnerCache, |
|
182 IDBFactory* aFactory, |
|
183 already_AddRefed<DatabaseInfo> aDatabaseInfo, |
|
184 const nsACString& aASCIIOrigin, |
|
185 FileManager* aFileManager, |
|
186 mozilla::dom::ContentParent* aContentParent) |
|
187 { |
|
188 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
189 NS_ASSERTION(aFactory, "Null pointer!"); |
|
190 NS_ASSERTION(!aASCIIOrigin.IsEmpty(), "Empty origin!"); |
|
191 |
|
192 nsRefPtr<DatabaseInfo> databaseInfo(aDatabaseInfo); |
|
193 NS_ASSERTION(databaseInfo, "Null pointer!"); |
|
194 |
|
195 nsRefPtr<IDBDatabase> db(new IDBDatabase(aOwnerCache)); |
|
196 |
|
197 db->SetScriptOwner(aOwnerCache->GetScriptOwner()); |
|
198 db->mFactory = aFactory; |
|
199 db->mDatabaseId = databaseInfo->id; |
|
200 db->mName = databaseInfo->name; |
|
201 db->mFilePath = databaseInfo->filePath; |
|
202 db->mPersistenceType = databaseInfo->persistenceType; |
|
203 db->mGroup = databaseInfo->group; |
|
204 databaseInfo.swap(db->mDatabaseInfo); |
|
205 db->mASCIIOrigin = aASCIIOrigin; |
|
206 db->mFileManager = aFileManager; |
|
207 db->mContentParent = aContentParent; |
|
208 |
|
209 QuotaManager* quotaManager = QuotaManager::Get(); |
|
210 NS_ASSERTION(quotaManager, "This should never be null!"); |
|
211 |
|
212 db->mQuotaClient = quotaManager->GetClient(Client::IDB); |
|
213 NS_ASSERTION(db->mQuotaClient, "This shouldn't fail!"); |
|
214 |
|
215 if (!quotaManager->RegisterStorage(db)) { |
|
216 // Either out of memory or shutting down. |
|
217 return nullptr; |
|
218 } |
|
219 |
|
220 db->mRegistered = true; |
|
221 |
|
222 return db.forget(); |
|
223 } |
|
224 |
|
225 // static |
|
226 IDBDatabase* |
|
227 IDBDatabase::FromStorage(nsIOfflineStorage* aStorage) |
|
228 { |
|
229 return aStorage->GetClient()->GetType() == Client::IDB ? |
|
230 static_cast<IDBDatabase*>(aStorage) : nullptr; |
|
231 } |
|
232 |
|
233 IDBDatabase::IDBDatabase(IDBWrapperCache* aOwnerCache) |
|
234 : IDBWrapperCache(aOwnerCache), |
|
235 mActorChild(nullptr), |
|
236 mActorParent(nullptr), |
|
237 mContentParent(nullptr), |
|
238 mInvalidated(false), |
|
239 mRegistered(false), |
|
240 mClosed(false), |
|
241 mRunningVersionChange(false) |
|
242 { |
|
243 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
244 } |
|
245 |
|
246 IDBDatabase::~IDBDatabase() |
|
247 { |
|
248 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
249 } |
|
250 |
|
251 void |
|
252 IDBDatabase::LastRelease() |
|
253 { |
|
254 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
255 |
|
256 NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!"); |
|
257 if (mActorChild) { |
|
258 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); |
|
259 mActorChild->Send__delete__(mActorChild); |
|
260 NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!"); |
|
261 } |
|
262 |
|
263 if (mRegistered) { |
|
264 CloseInternal(true); |
|
265 |
|
266 QuotaManager* quotaManager = QuotaManager::Get(); |
|
267 if (quotaManager) { |
|
268 quotaManager->UnregisterStorage(this); |
|
269 } |
|
270 mRegistered = false; |
|
271 } |
|
272 } |
|
273 |
|
274 NS_IMETHODIMP_(void) |
|
275 IDBDatabase::Invalidate() |
|
276 { |
|
277 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
278 |
|
279 InvalidateInternal(/* aIsDead */ false); |
|
280 } |
|
281 |
|
282 void |
|
283 IDBDatabase::InvalidateInternal(bool aIsDead) |
|
284 { |
|
285 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
286 |
|
287 if (IsInvalidated()) { |
|
288 return; |
|
289 } |
|
290 |
|
291 mInvalidated = true; |
|
292 |
|
293 // Make sure we're closed too. |
|
294 Close(); |
|
295 |
|
296 // When the IndexedDatabaseManager needs to invalidate databases, all it has |
|
297 // is an origin, so we call into the quota manager here to cancel any prompts |
|
298 // for our owner. |
|
299 nsPIDOMWindow* owner = GetOwner(); |
|
300 if (owner) { |
|
301 QuotaManager::CancelPromptsForWindow(owner); |
|
302 } |
|
303 |
|
304 // We want to forcefully remove in the child when the parent has invalidated |
|
305 // us in IPC mode because the database might no longer exist. |
|
306 // We don't want to forcefully remove in the parent when a child dies since |
|
307 // other child processes may be using the referenced DatabaseInfo. |
|
308 if (!aIsDead) { |
|
309 DatabaseInfo::Remove(mDatabaseId); |
|
310 } |
|
311 |
|
312 // And let the child process know as well. |
|
313 if (mActorParent) { |
|
314 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); |
|
315 mActorParent->Invalidate(); |
|
316 } |
|
317 } |
|
318 |
|
319 void |
|
320 IDBDatabase::DisconnectFromActorParent() |
|
321 { |
|
322 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); |
|
323 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
324 |
|
325 // Make sure we're closed too. |
|
326 Close(); |
|
327 |
|
328 // Kill any outstanding prompts. |
|
329 nsPIDOMWindow* owner = GetOwner(); |
|
330 if (owner) { |
|
331 QuotaManager::CancelPromptsForWindow(owner); |
|
332 } |
|
333 } |
|
334 |
|
335 void |
|
336 IDBDatabase::CloseInternal(bool aIsDead) |
|
337 { |
|
338 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
339 |
|
340 if (!mClosed) { |
|
341 mClosed = true; |
|
342 |
|
343 // If we're getting called from Unlink, avoid cloning the DatabaseInfo. |
|
344 { |
|
345 nsRefPtr<DatabaseInfo> previousInfo; |
|
346 mDatabaseInfo.swap(previousInfo); |
|
347 |
|
348 if (!aIsDead) { |
|
349 mDatabaseInfo = previousInfo->Clone(); |
|
350 } |
|
351 } |
|
352 |
|
353 QuotaManager* quotaManager = QuotaManager::Get(); |
|
354 if (quotaManager) { |
|
355 quotaManager->OnStorageClosed(this); |
|
356 } |
|
357 |
|
358 // And let the parent process know as well. |
|
359 if (mActorChild && !IsInvalidated()) { |
|
360 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); |
|
361 mActorChild->SendClose(aIsDead); |
|
362 } |
|
363 } |
|
364 } |
|
365 |
|
366 NS_IMETHODIMP_(bool) |
|
367 IDBDatabase::IsClosed() |
|
368 { |
|
369 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
370 return mClosed; |
|
371 } |
|
372 |
|
373 void |
|
374 IDBDatabase::EnterSetVersionTransaction() |
|
375 { |
|
376 NS_ASSERTION(!mRunningVersionChange, "How did that happen?"); |
|
377 |
|
378 mPreviousDatabaseInfo = mDatabaseInfo->Clone(); |
|
379 |
|
380 mRunningVersionChange = true; |
|
381 } |
|
382 |
|
383 void |
|
384 IDBDatabase::ExitSetVersionTransaction() |
|
385 { |
|
386 NS_ASSERTION(mRunningVersionChange, "How did that happen?"); |
|
387 |
|
388 mPreviousDatabaseInfo = nullptr; |
|
389 |
|
390 mRunningVersionChange = false; |
|
391 } |
|
392 |
|
393 void |
|
394 IDBDatabase::RevertToPreviousState() |
|
395 { |
|
396 mDatabaseInfo = mPreviousDatabaseInfo; |
|
397 mPreviousDatabaseInfo = nullptr; |
|
398 } |
|
399 |
|
400 void |
|
401 IDBDatabase::OnUnlink() |
|
402 { |
|
403 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
404 |
|
405 // We've been unlinked, at the very least we should be able to prevent further |
|
406 // transactions from starting and unblock any other SetVersion callers. |
|
407 CloseInternal(true); |
|
408 |
|
409 // No reason for the QuotaManager to track us any longer. |
|
410 QuotaManager* quotaManager = QuotaManager::Get(); |
|
411 if (mRegistered && quotaManager) { |
|
412 quotaManager->UnregisterStorage(this); |
|
413 |
|
414 // Don't try to unregister again in the destructor. |
|
415 mRegistered = false; |
|
416 } |
|
417 } |
|
418 |
|
419 already_AddRefed<IDBObjectStore> |
|
420 IDBDatabase::CreateObjectStoreInternal(IDBTransaction* aTransaction, |
|
421 const ObjectStoreInfoGuts& aInfo, |
|
422 ErrorResult& aRv) |
|
423 { |
|
424 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
425 NS_ASSERTION(aTransaction, "Null transaction!"); |
|
426 |
|
427 DatabaseInfo* databaseInfo = aTransaction->DBInfo(); |
|
428 |
|
429 nsRefPtr<ObjectStoreInfo> newInfo = new ObjectStoreInfo(); |
|
430 *static_cast<ObjectStoreInfoGuts*>(newInfo.get()) = aInfo; |
|
431 |
|
432 newInfo->nextAutoIncrementId = aInfo.autoIncrement ? 1 : 0; |
|
433 newInfo->comittedAutoIncrementId = newInfo->nextAutoIncrementId; |
|
434 |
|
435 if (!databaseInfo->PutObjectStore(newInfo)) { |
|
436 IDB_WARNING("Put failed!"); |
|
437 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); |
|
438 return nullptr; |
|
439 } |
|
440 |
|
441 // Don't leave this in the hash if we fail below! |
|
442 AutoRemoveObjectStore autoRemove(databaseInfo, newInfo->name); |
|
443 |
|
444 nsRefPtr<IDBObjectStore> objectStore = |
|
445 aTransaction->GetOrCreateObjectStore(newInfo->name, newInfo, true); |
|
446 if (!objectStore) { |
|
447 IDB_WARNING("Failed to get objectStore!"); |
|
448 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); |
|
449 return nullptr; |
|
450 } |
|
451 |
|
452 if (IndexedDatabaseManager::IsMainProcess()) { |
|
453 nsRefPtr<CreateObjectStoreHelper> helper = |
|
454 new CreateObjectStoreHelper(aTransaction, objectStore); |
|
455 |
|
456 nsresult rv = helper->DispatchToTransactionPool(); |
|
457 if (NS_FAILED(rv)) { |
|
458 IDB_WARNING("Failed to dispatch!"); |
|
459 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); |
|
460 return nullptr; |
|
461 } |
|
462 } |
|
463 |
|
464 autoRemove.forget(); |
|
465 |
|
466 IDB_PROFILER_MARK("IndexedDB Pseudo-request: " |
|
467 "database(%s).transaction(%s).createObjectStore(%s)", |
|
468 "MT IDBDatabase.createObjectStore()", |
|
469 IDB_PROFILER_STRING(this), |
|
470 IDB_PROFILER_STRING(aTransaction), |
|
471 IDB_PROFILER_STRING(objectStore)); |
|
472 |
|
473 return objectStore.forget(); |
|
474 } |
|
475 |
|
476 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBDatabase) |
|
477 |
|
478 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBDatabase, IDBWrapperCache) |
|
479 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFactory) |
|
480 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
481 |
|
482 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBDatabase, IDBWrapperCache) |
|
483 // Don't unlink mFactory! |
|
484 |
|
485 // Do some cleanup. |
|
486 tmp->OnUnlink(); |
|
487 NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
|
488 |
|
489 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBDatabase) |
|
490 NS_INTERFACE_MAP_ENTRY(nsIFileStorage) |
|
491 NS_INTERFACE_MAP_ENTRY(nsIOfflineStorage) |
|
492 NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache) |
|
493 |
|
494 NS_IMPL_ADDREF_INHERITED(IDBDatabase, IDBWrapperCache) |
|
495 NS_IMPL_RELEASE_INHERITED(IDBDatabase, IDBWrapperCache) |
|
496 |
|
497 JSObject* |
|
498 IDBDatabase::WrapObject(JSContext* aCx) |
|
499 { |
|
500 return IDBDatabaseBinding::Wrap(aCx, this); |
|
501 } |
|
502 |
|
503 uint64_t |
|
504 IDBDatabase::Version() const |
|
505 { |
|
506 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
507 DatabaseInfo* info = Info(); |
|
508 return info->version; |
|
509 } |
|
510 |
|
511 already_AddRefed<DOMStringList> |
|
512 IDBDatabase::GetObjectStoreNames(ErrorResult& aRv) const |
|
513 { |
|
514 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
515 |
|
516 DatabaseInfo* info = Info(); |
|
517 |
|
518 nsRefPtr<DOMStringList> list(new DOMStringList()); |
|
519 if (!info->GetObjectStoreNames(list->StringArray())) { |
|
520 IDB_WARNING("Couldn't get names!"); |
|
521 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); |
|
522 return nullptr; |
|
523 } |
|
524 |
|
525 return list.forget(); |
|
526 } |
|
527 |
|
528 already_AddRefed<IDBObjectStore> |
|
529 IDBDatabase::CreateObjectStore( |
|
530 JSContext* aCx, const nsAString& aName, |
|
531 const IDBObjectStoreParameters& aOptionalParameters, |
|
532 ErrorResult& aRv) |
|
533 { |
|
534 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
535 |
|
536 IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction(); |
|
537 |
|
538 if (!transaction || |
|
539 transaction->GetMode() != IDBTransaction::VERSION_CHANGE) { |
|
540 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); |
|
541 return nullptr; |
|
542 } |
|
543 |
|
544 DatabaseInfo* databaseInfo = transaction->DBInfo(); |
|
545 |
|
546 KeyPath keyPath(0); |
|
547 if (NS_FAILED(KeyPath::Parse(aCx, aOptionalParameters.mKeyPath, &keyPath))) { |
|
548 aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); |
|
549 return nullptr; |
|
550 } |
|
551 |
|
552 if (databaseInfo->ContainsStoreName(aName)) { |
|
553 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR); |
|
554 return nullptr; |
|
555 } |
|
556 |
|
557 if (!keyPath.IsAllowedForObjectStore(aOptionalParameters.mAutoIncrement)) { |
|
558 aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); |
|
559 return nullptr; |
|
560 } |
|
561 |
|
562 ObjectStoreInfoGuts guts; |
|
563 |
|
564 guts.name = aName; |
|
565 guts.id = databaseInfo->nextObjectStoreId++; |
|
566 guts.keyPath = keyPath; |
|
567 guts.autoIncrement = aOptionalParameters.mAutoIncrement; |
|
568 |
|
569 return CreateObjectStoreInternal(transaction, guts, aRv); |
|
570 } |
|
571 |
|
572 void |
|
573 IDBDatabase::DeleteObjectStore(const nsAString& aName, ErrorResult& aRv) |
|
574 { |
|
575 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
576 |
|
577 IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction(); |
|
578 |
|
579 if (!transaction || |
|
580 transaction->GetMode() != IDBTransaction::VERSION_CHANGE) { |
|
581 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); |
|
582 return; |
|
583 } |
|
584 |
|
585 DatabaseInfo* info = transaction->DBInfo(); |
|
586 ObjectStoreInfo* objectStoreInfo = info->GetObjectStore(aName); |
|
587 if (!objectStoreInfo) { |
|
588 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR); |
|
589 return; |
|
590 } |
|
591 |
|
592 if (IndexedDatabaseManager::IsMainProcess()) { |
|
593 nsRefPtr<DeleteObjectStoreHelper> helper = |
|
594 new DeleteObjectStoreHelper(transaction, objectStoreInfo->id); |
|
595 |
|
596 nsresult rv = helper->DispatchToTransactionPool(); |
|
597 if (NS_FAILED(rv)) { |
|
598 IDB_WARNING("Failed to dispatch!"); |
|
599 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); |
|
600 return; |
|
601 } |
|
602 } |
|
603 else { |
|
604 IndexedDBTransactionChild* actor = transaction->GetActorChild(); |
|
605 NS_ASSERTION(actor, "Must have an actor here!"); |
|
606 |
|
607 actor->SendDeleteObjectStore(nsString(aName)); |
|
608 } |
|
609 |
|
610 transaction->RemoveObjectStore(aName); |
|
611 |
|
612 IDB_PROFILER_MARK("IndexedDB Pseudo-request: " |
|
613 "database(%s).transaction(%s).deleteObjectStore(\"%s\")", |
|
614 "MT IDBDatabase.deleteObjectStore()", |
|
615 IDB_PROFILER_STRING(this), |
|
616 IDB_PROFILER_STRING(transaction), |
|
617 NS_ConvertUTF16toUTF8(aName).get()); |
|
618 } |
|
619 |
|
620 already_AddRefed<indexedDB::IDBTransaction> |
|
621 IDBDatabase::Transaction(const Sequence<nsString>& aStoreNames, |
|
622 IDBTransactionMode aMode, ErrorResult& aRv) |
|
623 { |
|
624 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
625 |
|
626 if (QuotaManager::IsShuttingDown()) { |
|
627 IDB_REPORT_INTERNAL_ERR(); |
|
628 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); |
|
629 return nullptr; |
|
630 } |
|
631 |
|
632 if (mClosed) { |
|
633 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); |
|
634 return nullptr; |
|
635 } |
|
636 |
|
637 if (mRunningVersionChange) { |
|
638 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); |
|
639 return nullptr; |
|
640 } |
|
641 |
|
642 if (aStoreNames.IsEmpty()) { |
|
643 aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); |
|
644 return nullptr; |
|
645 } |
|
646 |
|
647 IDBTransaction::Mode transactionMode = IDBTransaction::READ_ONLY; |
|
648 switch (aMode) { |
|
649 case IDBTransactionMode::Readonly: |
|
650 transactionMode = IDBTransaction::READ_ONLY; |
|
651 break; |
|
652 case IDBTransactionMode::Readwrite: |
|
653 transactionMode = IDBTransaction::READ_WRITE; |
|
654 break; |
|
655 case IDBTransactionMode::Versionchange: |
|
656 transactionMode = IDBTransaction::VERSION_CHANGE; |
|
657 break; |
|
658 default: |
|
659 MOZ_CRASH("Unknown mode!"); |
|
660 } |
|
661 |
|
662 // Now check to make sure the object store names we collected actually exist. |
|
663 DatabaseInfo* info = Info(); |
|
664 for (uint32_t index = 0; index < aStoreNames.Length(); index++) { |
|
665 if (!info->ContainsStoreName(aStoreNames[index])) { |
|
666 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR); |
|
667 return nullptr; |
|
668 } |
|
669 } |
|
670 |
|
671 nsRefPtr<IDBTransaction> transaction = |
|
672 IDBTransaction::Create(this, aStoreNames, transactionMode, false); |
|
673 if (!transaction) { |
|
674 IDB_WARNING("Failed to create the transaction!"); |
|
675 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); |
|
676 return nullptr; |
|
677 } |
|
678 |
|
679 IDB_PROFILER_MARK("IndexedDB Transaction %llu: database(%s).transaction(%s)", |
|
680 "IDBTransaction[%llu] MT Started", |
|
681 transaction->GetSerialNumber(), IDB_PROFILER_STRING(this), |
|
682 IDB_PROFILER_STRING(transaction)); |
|
683 |
|
684 return transaction.forget(); |
|
685 } |
|
686 |
|
687 already_AddRefed<IDBRequest> |
|
688 IDBDatabase::MozCreateFileHandle(const nsAString& aName, |
|
689 const Optional<nsAString>& aType, |
|
690 ErrorResult& aRv) |
|
691 { |
|
692 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
693 |
|
694 if (!IndexedDatabaseManager::IsMainProcess()) { |
|
695 IDB_WARNING("Not supported yet!"); |
|
696 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); |
|
697 return nullptr; |
|
698 } |
|
699 |
|
700 if (QuotaManager::IsShuttingDown()) { |
|
701 IDB_REPORT_INTERNAL_ERR(); |
|
702 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); |
|
703 return nullptr; |
|
704 } |
|
705 |
|
706 if (mClosed) { |
|
707 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); |
|
708 return nullptr; |
|
709 } |
|
710 |
|
711 nsRefPtr<IDBRequest> request = IDBRequest::Create(this, nullptr); |
|
712 |
|
713 nsRefPtr<CreateFileHelper> helper = |
|
714 new CreateFileHelper(this, request, aName, |
|
715 aType.WasPassed() ? aType.Value() : EmptyString()); |
|
716 |
|
717 QuotaManager* quotaManager = QuotaManager::Get(); |
|
718 NS_ASSERTION(quotaManager, "We should definitely have a manager here"); |
|
719 |
|
720 nsresult rv = helper->Dispatch(quotaManager->IOThread()); |
|
721 if (NS_FAILED(rv)) { |
|
722 IDB_WARNING("Failed to dispatch!"); |
|
723 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); |
|
724 return nullptr; |
|
725 } |
|
726 |
|
727 return request.forget(); |
|
728 } |
|
729 |
|
730 NS_IMETHODIMP |
|
731 IDBDatabase::Close() |
|
732 { |
|
733 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
734 |
|
735 CloseInternal(false); |
|
736 |
|
737 NS_ASSERTION(mClosed, "Should have set the closed flag!"); |
|
738 |
|
739 return NS_OK; |
|
740 } |
|
741 |
|
742 NS_IMETHODIMP_(const nsACString&) |
|
743 IDBDatabase::Id() |
|
744 { |
|
745 return mDatabaseId; |
|
746 } |
|
747 |
|
748 NS_IMETHODIMP_(bool) |
|
749 IDBDatabase::IsInvalidated() |
|
750 { |
|
751 return mInvalidated; |
|
752 } |
|
753 |
|
754 NS_IMETHODIMP_(bool) |
|
755 IDBDatabase::IsShuttingDown() |
|
756 { |
|
757 return QuotaManager::IsShuttingDown(); |
|
758 } |
|
759 |
|
760 NS_IMETHODIMP_(void) |
|
761 IDBDatabase::SetThreadLocals() |
|
762 { |
|
763 NS_ASSERTION(GetOwner(), "Should have owner!"); |
|
764 QuotaManager::SetCurrentWindow(GetOwner()); |
|
765 } |
|
766 |
|
767 NS_IMETHODIMP_(void) |
|
768 IDBDatabase::UnsetThreadLocals() |
|
769 { |
|
770 QuotaManager::SetCurrentWindow(nullptr); |
|
771 } |
|
772 |
|
773 NS_IMETHODIMP_(mozilla::dom::quota::Client*) |
|
774 IDBDatabase::GetClient() |
|
775 { |
|
776 return mQuotaClient; |
|
777 } |
|
778 |
|
779 NS_IMETHODIMP_(bool) |
|
780 IDBDatabase::IsOwned(nsPIDOMWindow* aOwner) |
|
781 { |
|
782 return GetOwner() == aOwner; |
|
783 } |
|
784 |
|
785 NS_IMETHODIMP_(const nsACString&) |
|
786 IDBDatabase::Origin() |
|
787 { |
|
788 return mASCIIOrigin; |
|
789 } |
|
790 |
|
791 nsresult |
|
792 IDBDatabase::PostHandleEvent(EventChainPostVisitor& aVisitor) |
|
793 { |
|
794 return IndexedDatabaseManager::FireWindowOnError(GetOwner(), aVisitor); |
|
795 } |
|
796 |
|
797 AsyncConnectionHelper::ChildProcessSendResult |
|
798 NoRequestDatabaseHelper::SendResponseToChildProcess(nsresult aResultCode) |
|
799 { |
|
800 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); |
|
801 return Success_NotSent; |
|
802 } |
|
803 |
|
804 nsresult |
|
805 NoRequestDatabaseHelper::UnpackResponseFromParentProcess( |
|
806 const ResponseValue& aResponseValue) |
|
807 { |
|
808 MOZ_CRASH("Should never get here!"); |
|
809 } |
|
810 |
|
811 nsresult |
|
812 NoRequestDatabaseHelper::OnSuccess() |
|
813 { |
|
814 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); |
|
815 return NS_OK; |
|
816 } |
|
817 |
|
818 void |
|
819 NoRequestDatabaseHelper::OnError() |
|
820 { |
|
821 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); |
|
822 mTransaction->Abort(GetResultCode()); |
|
823 } |
|
824 |
|
825 nsresult |
|
826 CreateObjectStoreHelper::DoDatabaseWork(mozIStorageConnection* aConnection) |
|
827 { |
|
828 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); |
|
829 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); |
|
830 |
|
831 PROFILER_LABEL("IndexedDB", "CreateObjectStoreHelper::DoDatabaseWork"); |
|
832 |
|
833 if (IndexedDatabaseManager::InLowDiskSpaceMode()) { |
|
834 NS_WARNING("Refusing to create additional objectStore because disk space " |
|
835 "is low!"); |
|
836 return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; |
|
837 } |
|
838 |
|
839 nsCOMPtr<mozIStorageStatement> stmt = |
|
840 mTransaction->GetCachedStatement(NS_LITERAL_CSTRING( |
|
841 "INSERT INTO object_store (id, auto_increment, name, key_path) " |
|
842 "VALUES (:id, :auto_increment, :name, :key_path)" |
|
843 )); |
|
844 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); |
|
845 |
|
846 mozStorageStatementScoper scoper(stmt); |
|
847 |
|
848 nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), |
|
849 mObjectStore->Id()); |
|
850 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); |
|
851 |
|
852 rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("auto_increment"), |
|
853 mObjectStore->IsAutoIncrement() ? 1 : 0); |
|
854 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); |
|
855 |
|
856 rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mObjectStore->Name()); |
|
857 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); |
|
858 |
|
859 const KeyPath& keyPath = mObjectStore->GetKeyPath(); |
|
860 if (keyPath.IsValid()) { |
|
861 nsAutoString keyPathSerialization; |
|
862 keyPath.SerializeToString(keyPathSerialization); |
|
863 rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key_path"), |
|
864 keyPathSerialization); |
|
865 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); |
|
866 } |
|
867 else { |
|
868 rv = stmt->BindNullByName(NS_LITERAL_CSTRING("key_path")); |
|
869 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); |
|
870 } |
|
871 |
|
872 rv = stmt->Execute(); |
|
873 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); |
|
874 |
|
875 return NS_OK; |
|
876 } |
|
877 |
|
878 void |
|
879 CreateObjectStoreHelper::ReleaseMainThreadObjects() |
|
880 { |
|
881 mObjectStore = nullptr; |
|
882 NoRequestDatabaseHelper::ReleaseMainThreadObjects(); |
|
883 } |
|
884 |
|
885 nsresult |
|
886 DeleteObjectStoreHelper::DoDatabaseWork(mozIStorageConnection* aConnection) |
|
887 { |
|
888 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); |
|
889 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); |
|
890 |
|
891 PROFILER_LABEL("IndexedDB", "DeleteObjectStoreHelper::DoDatabaseWork"); |
|
892 |
|
893 nsCOMPtr<mozIStorageStatement> stmt = |
|
894 mTransaction->GetCachedStatement(NS_LITERAL_CSTRING( |
|
895 "DELETE FROM object_store " |
|
896 "WHERE id = :id " |
|
897 )); |
|
898 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); |
|
899 |
|
900 mozStorageStatementScoper scoper(stmt); |
|
901 |
|
902 nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mObjectStoreId); |
|
903 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); |
|
904 |
|
905 rv = stmt->Execute(); |
|
906 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); |
|
907 |
|
908 return NS_OK; |
|
909 } |
|
910 |
|
911 nsresult |
|
912 CreateFileHelper::DoDatabaseWork(mozIStorageConnection* aConnection) |
|
913 { |
|
914 AssertIsOnIOThread(); |
|
915 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); |
|
916 |
|
917 PROFILER_LABEL("IndexedDB", "CreateFileHelper::DoDatabaseWork"); |
|
918 |
|
919 if (IndexedDatabaseManager::InLowDiskSpaceMode()) { |
|
920 NS_WARNING("Refusing to create file because disk space is low!"); |
|
921 return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; |
|
922 } |
|
923 |
|
924 FileManager* fileManager = mDatabase->Manager(); |
|
925 |
|
926 mFileInfo = fileManager->GetNewFileInfo(); |
|
927 IDB_ENSURE_TRUE(mFileInfo, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); |
|
928 |
|
929 const int64_t& fileId = mFileInfo->Id(); |
|
930 |
|
931 nsCOMPtr<nsIFile> directory = fileManager->EnsureJournalDirectory(); |
|
932 NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE); |
|
933 |
|
934 nsCOMPtr<nsIFile> file = fileManager->GetFileForId(directory, fileId); |
|
935 NS_ENSURE_TRUE(file, NS_ERROR_FAILURE); |
|
936 |
|
937 nsresult rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0644); |
|
938 NS_ENSURE_SUCCESS(rv, rv); |
|
939 |
|
940 directory = fileManager->GetDirectory(); |
|
941 IDB_ENSURE_TRUE(directory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); |
|
942 |
|
943 file = fileManager->GetFileForId(directory, fileId); |
|
944 IDB_ENSURE_TRUE(file, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); |
|
945 |
|
946 rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0644); |
|
947 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); |
|
948 |
|
949 return NS_OK; |
|
950 } |
|
951 |
|
952 nsresult |
|
953 CreateFileHelper::GetSuccessResult(JSContext* aCx, |
|
954 JS::MutableHandle<JS::Value> aVal) |
|
955 { |
|
956 nsRefPtr<IDBFileHandle> fileHandle = |
|
957 IDBFileHandle::Create(mDatabase, mName, mType, mFileInfo.forget()); |
|
958 IDB_ENSURE_TRUE(fileHandle, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); |
|
959 |
|
960 return WrapNative(aCx, NS_ISUPPORTS_CAST(EventTarget*, fileHandle), aVal); |
|
961 } |