1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/indexedDB/IDBDatabase.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,961 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "base/basictypes.h" 1.11 + 1.12 +#include "IDBDatabase.h" 1.13 + 1.14 +#include "mozilla/EventDispatcher.h" 1.15 +#include "mozilla/Mutex.h" 1.16 +#include "mozilla/storage.h" 1.17 +#include "mozilla/dom/ContentParent.h" 1.18 +#include "mozilla/dom/DOMStringList.h" 1.19 +#include "mozilla/dom/DOMStringListBinding.h" 1.20 +#include "mozilla/dom/quota/Client.h" 1.21 +#include "mozilla/dom/quota/QuotaManager.h" 1.22 +#include "nsJSUtils.h" 1.23 +#include "nsProxyRelease.h" 1.24 +#include "nsThreadUtils.h" 1.25 + 1.26 +#include "AsyncConnectionHelper.h" 1.27 +#include "DatabaseInfo.h" 1.28 +#include "IDBEvents.h" 1.29 +#include "IDBFactory.h" 1.30 +#include "IDBFileHandle.h" 1.31 +#include "IDBIndex.h" 1.32 +#include "IDBObjectStore.h" 1.33 +#include "IDBTransaction.h" 1.34 +#include "IDBFactory.h" 1.35 +#include "ProfilerHelpers.h" 1.36 +#include "ReportInternalError.h" 1.37 +#include "TransactionThreadPool.h" 1.38 + 1.39 +#include "ipc/IndexedDBChild.h" 1.40 +#include "ipc/IndexedDBParent.h" 1.41 + 1.42 +#include "mozilla/dom/IDBDatabaseBinding.h" 1.43 + 1.44 +USING_INDEXEDDB_NAMESPACE 1.45 +using mozilla::dom::ContentParent; 1.46 +using mozilla::dom::quota::AssertIsOnIOThread; 1.47 +using mozilla::dom::quota::Client; 1.48 +using mozilla::dom::quota::QuotaManager; 1.49 +using mozilla::ErrorResult; 1.50 +using namespace mozilla; 1.51 +using namespace mozilla::dom; 1.52 + 1.53 +namespace { 1.54 + 1.55 +class NoRequestDatabaseHelper : public AsyncConnectionHelper 1.56 +{ 1.57 +public: 1.58 + NoRequestDatabaseHelper(IDBTransaction* aTransaction) 1.59 + : AsyncConnectionHelper(aTransaction, nullptr) 1.60 + { 1.61 + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); 1.62 + NS_ASSERTION(aTransaction, "Null transaction!"); 1.63 + } 1.64 + 1.65 + virtual ChildProcessSendResult 1.66 + SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; 1.67 + 1.68 + virtual nsresult 1.69 + UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) 1.70 + MOZ_OVERRIDE; 1.71 + 1.72 + virtual nsresult OnSuccess() MOZ_OVERRIDE; 1.73 + 1.74 + virtual void OnError() MOZ_OVERRIDE; 1.75 +}; 1.76 + 1.77 +class CreateObjectStoreHelper : public NoRequestDatabaseHelper 1.78 +{ 1.79 +public: 1.80 + CreateObjectStoreHelper(IDBTransaction* aTransaction, 1.81 + IDBObjectStore* aObjectStore) 1.82 + : NoRequestDatabaseHelper(aTransaction), mObjectStore(aObjectStore) 1.83 + { } 1.84 + 1.85 + virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) 1.86 + MOZ_OVERRIDE; 1.87 + 1.88 + virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; 1.89 + 1.90 +private: 1.91 + nsRefPtr<IDBObjectStore> mObjectStore; 1.92 +}; 1.93 + 1.94 +class DeleteObjectStoreHelper : public NoRequestDatabaseHelper 1.95 +{ 1.96 +public: 1.97 + DeleteObjectStoreHelper(IDBTransaction* aTransaction, 1.98 + int64_t aObjectStoreId) 1.99 + : NoRequestDatabaseHelper(aTransaction), mObjectStoreId(aObjectStoreId) 1.100 + { } 1.101 + 1.102 + virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) 1.103 + MOZ_OVERRIDE; 1.104 + 1.105 +private: 1.106 + // In-params. 1.107 + int64_t mObjectStoreId; 1.108 +}; 1.109 + 1.110 +class CreateFileHelper : public AsyncConnectionHelper 1.111 +{ 1.112 +public: 1.113 + CreateFileHelper(IDBDatabase* aDatabase, 1.114 + IDBRequest* aRequest, 1.115 + const nsAString& aName, 1.116 + const nsAString& aType) 1.117 + : AsyncConnectionHelper(aDatabase, aRequest), 1.118 + mName(aName), mType(aType) 1.119 + { } 1.120 + 1.121 + ~CreateFileHelper() 1.122 + { } 1.123 + 1.124 + nsresult DoDatabaseWork(mozIStorageConnection* aConnection); 1.125 + nsresult GetSuccessResult(JSContext* aCx, 1.126 + JS::MutableHandle<JS::Value> aVal); 1.127 + void ReleaseMainThreadObjects() 1.128 + { 1.129 + mFileInfo = nullptr; 1.130 + AsyncConnectionHelper::ReleaseMainThreadObjects(); 1.131 + } 1.132 + 1.133 + virtual ChildProcessSendResult SendResponseToChildProcess( 1.134 + nsresult aResultCode) 1.135 + MOZ_OVERRIDE 1.136 + { 1.137 + return Success_NotSent; 1.138 + } 1.139 + 1.140 + virtual nsresult UnpackResponseFromParentProcess( 1.141 + const ResponseValue& aResponseValue) 1.142 + MOZ_OVERRIDE 1.143 + { 1.144 + MOZ_CRASH("Should never get here!"); 1.145 + } 1.146 + 1.147 +private: 1.148 + // In-params. 1.149 + nsString mName; 1.150 + nsString mType; 1.151 + 1.152 + // Out-params. 1.153 + nsRefPtr<FileInfo> mFileInfo; 1.154 +}; 1.155 + 1.156 +class MOZ_STACK_CLASS AutoRemoveObjectStore 1.157 +{ 1.158 +public: 1.159 + AutoRemoveObjectStore(DatabaseInfo* aInfo, const nsAString& aName) 1.160 + : mInfo(aInfo), mName(aName) 1.161 + { } 1.162 + 1.163 + ~AutoRemoveObjectStore() 1.164 + { 1.165 + if (mInfo) { 1.166 + mInfo->RemoveObjectStore(mName); 1.167 + } 1.168 + } 1.169 + 1.170 + void forget() 1.171 + { 1.172 + mInfo = nullptr; 1.173 + } 1.174 + 1.175 +private: 1.176 + DatabaseInfo* mInfo; 1.177 + nsString mName; 1.178 +}; 1.179 + 1.180 +} // anonymous namespace 1.181 + 1.182 +// static 1.183 +already_AddRefed<IDBDatabase> 1.184 +IDBDatabase::Create(IDBWrapperCache* aOwnerCache, 1.185 + IDBFactory* aFactory, 1.186 + already_AddRefed<DatabaseInfo> aDatabaseInfo, 1.187 + const nsACString& aASCIIOrigin, 1.188 + FileManager* aFileManager, 1.189 + mozilla::dom::ContentParent* aContentParent) 1.190 +{ 1.191 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.192 + NS_ASSERTION(aFactory, "Null pointer!"); 1.193 + NS_ASSERTION(!aASCIIOrigin.IsEmpty(), "Empty origin!"); 1.194 + 1.195 + nsRefPtr<DatabaseInfo> databaseInfo(aDatabaseInfo); 1.196 + NS_ASSERTION(databaseInfo, "Null pointer!"); 1.197 + 1.198 + nsRefPtr<IDBDatabase> db(new IDBDatabase(aOwnerCache)); 1.199 + 1.200 + db->SetScriptOwner(aOwnerCache->GetScriptOwner()); 1.201 + db->mFactory = aFactory; 1.202 + db->mDatabaseId = databaseInfo->id; 1.203 + db->mName = databaseInfo->name; 1.204 + db->mFilePath = databaseInfo->filePath; 1.205 + db->mPersistenceType = databaseInfo->persistenceType; 1.206 + db->mGroup = databaseInfo->group; 1.207 + databaseInfo.swap(db->mDatabaseInfo); 1.208 + db->mASCIIOrigin = aASCIIOrigin; 1.209 + db->mFileManager = aFileManager; 1.210 + db->mContentParent = aContentParent; 1.211 + 1.212 + QuotaManager* quotaManager = QuotaManager::Get(); 1.213 + NS_ASSERTION(quotaManager, "This should never be null!"); 1.214 + 1.215 + db->mQuotaClient = quotaManager->GetClient(Client::IDB); 1.216 + NS_ASSERTION(db->mQuotaClient, "This shouldn't fail!"); 1.217 + 1.218 + if (!quotaManager->RegisterStorage(db)) { 1.219 + // Either out of memory or shutting down. 1.220 + return nullptr; 1.221 + } 1.222 + 1.223 + db->mRegistered = true; 1.224 + 1.225 + return db.forget(); 1.226 +} 1.227 + 1.228 +// static 1.229 +IDBDatabase* 1.230 +IDBDatabase::FromStorage(nsIOfflineStorage* aStorage) 1.231 +{ 1.232 + return aStorage->GetClient()->GetType() == Client::IDB ? 1.233 + static_cast<IDBDatabase*>(aStorage) : nullptr; 1.234 +} 1.235 + 1.236 +IDBDatabase::IDBDatabase(IDBWrapperCache* aOwnerCache) 1.237 +: IDBWrapperCache(aOwnerCache), 1.238 + mActorChild(nullptr), 1.239 + mActorParent(nullptr), 1.240 + mContentParent(nullptr), 1.241 + mInvalidated(false), 1.242 + mRegistered(false), 1.243 + mClosed(false), 1.244 + mRunningVersionChange(false) 1.245 +{ 1.246 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.247 +} 1.248 + 1.249 +IDBDatabase::~IDBDatabase() 1.250 +{ 1.251 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.252 +} 1.253 + 1.254 +void 1.255 +IDBDatabase::LastRelease() 1.256 +{ 1.257 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.258 + 1.259 + NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!"); 1.260 + if (mActorChild) { 1.261 + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); 1.262 + mActorChild->Send__delete__(mActorChild); 1.263 + NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!"); 1.264 + } 1.265 + 1.266 + if (mRegistered) { 1.267 + CloseInternal(true); 1.268 + 1.269 + QuotaManager* quotaManager = QuotaManager::Get(); 1.270 + if (quotaManager) { 1.271 + quotaManager->UnregisterStorage(this); 1.272 + } 1.273 + mRegistered = false; 1.274 + } 1.275 +} 1.276 + 1.277 +NS_IMETHODIMP_(void) 1.278 +IDBDatabase::Invalidate() 1.279 +{ 1.280 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.281 + 1.282 + InvalidateInternal(/* aIsDead */ false); 1.283 +} 1.284 + 1.285 +void 1.286 +IDBDatabase::InvalidateInternal(bool aIsDead) 1.287 +{ 1.288 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.289 + 1.290 + if (IsInvalidated()) { 1.291 + return; 1.292 + } 1.293 + 1.294 + mInvalidated = true; 1.295 + 1.296 + // Make sure we're closed too. 1.297 + Close(); 1.298 + 1.299 + // When the IndexedDatabaseManager needs to invalidate databases, all it has 1.300 + // is an origin, so we call into the quota manager here to cancel any prompts 1.301 + // for our owner. 1.302 + nsPIDOMWindow* owner = GetOwner(); 1.303 + if (owner) { 1.304 + QuotaManager::CancelPromptsForWindow(owner); 1.305 + } 1.306 + 1.307 + // We want to forcefully remove in the child when the parent has invalidated 1.308 + // us in IPC mode because the database might no longer exist. 1.309 + // We don't want to forcefully remove in the parent when a child dies since 1.310 + // other child processes may be using the referenced DatabaseInfo. 1.311 + if (!aIsDead) { 1.312 + DatabaseInfo::Remove(mDatabaseId); 1.313 + } 1.314 + 1.315 + // And let the child process know as well. 1.316 + if (mActorParent) { 1.317 + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); 1.318 + mActorParent->Invalidate(); 1.319 + } 1.320 +} 1.321 + 1.322 +void 1.323 +IDBDatabase::DisconnectFromActorParent() 1.324 +{ 1.325 + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); 1.326 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.327 + 1.328 + // Make sure we're closed too. 1.329 + Close(); 1.330 + 1.331 + // Kill any outstanding prompts. 1.332 + nsPIDOMWindow* owner = GetOwner(); 1.333 + if (owner) { 1.334 + QuotaManager::CancelPromptsForWindow(owner); 1.335 + } 1.336 +} 1.337 + 1.338 +void 1.339 +IDBDatabase::CloseInternal(bool aIsDead) 1.340 +{ 1.341 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.342 + 1.343 + if (!mClosed) { 1.344 + mClosed = true; 1.345 + 1.346 + // If we're getting called from Unlink, avoid cloning the DatabaseInfo. 1.347 + { 1.348 + nsRefPtr<DatabaseInfo> previousInfo; 1.349 + mDatabaseInfo.swap(previousInfo); 1.350 + 1.351 + if (!aIsDead) { 1.352 + mDatabaseInfo = previousInfo->Clone(); 1.353 + } 1.354 + } 1.355 + 1.356 + QuotaManager* quotaManager = QuotaManager::Get(); 1.357 + if (quotaManager) { 1.358 + quotaManager->OnStorageClosed(this); 1.359 + } 1.360 + 1.361 + // And let the parent process know as well. 1.362 + if (mActorChild && !IsInvalidated()) { 1.363 + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); 1.364 + mActorChild->SendClose(aIsDead); 1.365 + } 1.366 + } 1.367 +} 1.368 + 1.369 +NS_IMETHODIMP_(bool) 1.370 +IDBDatabase::IsClosed() 1.371 +{ 1.372 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.373 + return mClosed; 1.374 +} 1.375 + 1.376 +void 1.377 +IDBDatabase::EnterSetVersionTransaction() 1.378 +{ 1.379 + NS_ASSERTION(!mRunningVersionChange, "How did that happen?"); 1.380 + 1.381 + mPreviousDatabaseInfo = mDatabaseInfo->Clone(); 1.382 + 1.383 + mRunningVersionChange = true; 1.384 +} 1.385 + 1.386 +void 1.387 +IDBDatabase::ExitSetVersionTransaction() 1.388 +{ 1.389 + NS_ASSERTION(mRunningVersionChange, "How did that happen?"); 1.390 + 1.391 + mPreviousDatabaseInfo = nullptr; 1.392 + 1.393 + mRunningVersionChange = false; 1.394 +} 1.395 + 1.396 +void 1.397 +IDBDatabase::RevertToPreviousState() 1.398 +{ 1.399 + mDatabaseInfo = mPreviousDatabaseInfo; 1.400 + mPreviousDatabaseInfo = nullptr; 1.401 +} 1.402 + 1.403 +void 1.404 +IDBDatabase::OnUnlink() 1.405 +{ 1.406 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.407 + 1.408 + // We've been unlinked, at the very least we should be able to prevent further 1.409 + // transactions from starting and unblock any other SetVersion callers. 1.410 + CloseInternal(true); 1.411 + 1.412 + // No reason for the QuotaManager to track us any longer. 1.413 + QuotaManager* quotaManager = QuotaManager::Get(); 1.414 + if (mRegistered && quotaManager) { 1.415 + quotaManager->UnregisterStorage(this); 1.416 + 1.417 + // Don't try to unregister again in the destructor. 1.418 + mRegistered = false; 1.419 + } 1.420 +} 1.421 + 1.422 +already_AddRefed<IDBObjectStore> 1.423 +IDBDatabase::CreateObjectStoreInternal(IDBTransaction* aTransaction, 1.424 + const ObjectStoreInfoGuts& aInfo, 1.425 + ErrorResult& aRv) 1.426 +{ 1.427 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.428 + NS_ASSERTION(aTransaction, "Null transaction!"); 1.429 + 1.430 + DatabaseInfo* databaseInfo = aTransaction->DBInfo(); 1.431 + 1.432 + nsRefPtr<ObjectStoreInfo> newInfo = new ObjectStoreInfo(); 1.433 + *static_cast<ObjectStoreInfoGuts*>(newInfo.get()) = aInfo; 1.434 + 1.435 + newInfo->nextAutoIncrementId = aInfo.autoIncrement ? 1 : 0; 1.436 + newInfo->comittedAutoIncrementId = newInfo->nextAutoIncrementId; 1.437 + 1.438 + if (!databaseInfo->PutObjectStore(newInfo)) { 1.439 + IDB_WARNING("Put failed!"); 1.440 + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.441 + return nullptr; 1.442 + } 1.443 + 1.444 + // Don't leave this in the hash if we fail below! 1.445 + AutoRemoveObjectStore autoRemove(databaseInfo, newInfo->name); 1.446 + 1.447 + nsRefPtr<IDBObjectStore> objectStore = 1.448 + aTransaction->GetOrCreateObjectStore(newInfo->name, newInfo, true); 1.449 + if (!objectStore) { 1.450 + IDB_WARNING("Failed to get objectStore!"); 1.451 + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.452 + return nullptr; 1.453 + } 1.454 + 1.455 + if (IndexedDatabaseManager::IsMainProcess()) { 1.456 + nsRefPtr<CreateObjectStoreHelper> helper = 1.457 + new CreateObjectStoreHelper(aTransaction, objectStore); 1.458 + 1.459 + nsresult rv = helper->DispatchToTransactionPool(); 1.460 + if (NS_FAILED(rv)) { 1.461 + IDB_WARNING("Failed to dispatch!"); 1.462 + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.463 + return nullptr; 1.464 + } 1.465 + } 1.466 + 1.467 + autoRemove.forget(); 1.468 + 1.469 + IDB_PROFILER_MARK("IndexedDB Pseudo-request: " 1.470 + "database(%s).transaction(%s).createObjectStore(%s)", 1.471 + "MT IDBDatabase.createObjectStore()", 1.472 + IDB_PROFILER_STRING(this), 1.473 + IDB_PROFILER_STRING(aTransaction), 1.474 + IDB_PROFILER_STRING(objectStore)); 1.475 + 1.476 + return objectStore.forget(); 1.477 +} 1.478 + 1.479 +NS_IMPL_CYCLE_COLLECTION_CLASS(IDBDatabase) 1.480 + 1.481 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBDatabase, IDBWrapperCache) 1.482 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFactory) 1.483 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.484 + 1.485 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBDatabase, IDBWrapperCache) 1.486 + // Don't unlink mFactory! 1.487 + 1.488 + // Do some cleanup. 1.489 + tmp->OnUnlink(); 1.490 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.491 + 1.492 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBDatabase) 1.493 + NS_INTERFACE_MAP_ENTRY(nsIFileStorage) 1.494 + NS_INTERFACE_MAP_ENTRY(nsIOfflineStorage) 1.495 +NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache) 1.496 + 1.497 +NS_IMPL_ADDREF_INHERITED(IDBDatabase, IDBWrapperCache) 1.498 +NS_IMPL_RELEASE_INHERITED(IDBDatabase, IDBWrapperCache) 1.499 + 1.500 +JSObject* 1.501 +IDBDatabase::WrapObject(JSContext* aCx) 1.502 +{ 1.503 + return IDBDatabaseBinding::Wrap(aCx, this); 1.504 +} 1.505 + 1.506 +uint64_t 1.507 +IDBDatabase::Version() const 1.508 +{ 1.509 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.510 + DatabaseInfo* info = Info(); 1.511 + return info->version; 1.512 +} 1.513 + 1.514 +already_AddRefed<DOMStringList> 1.515 +IDBDatabase::GetObjectStoreNames(ErrorResult& aRv) const 1.516 +{ 1.517 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.518 + 1.519 + DatabaseInfo* info = Info(); 1.520 + 1.521 + nsRefPtr<DOMStringList> list(new DOMStringList()); 1.522 + if (!info->GetObjectStoreNames(list->StringArray())) { 1.523 + IDB_WARNING("Couldn't get names!"); 1.524 + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.525 + return nullptr; 1.526 + } 1.527 + 1.528 + return list.forget(); 1.529 +} 1.530 + 1.531 +already_AddRefed<IDBObjectStore> 1.532 +IDBDatabase::CreateObjectStore( 1.533 + JSContext* aCx, const nsAString& aName, 1.534 + const IDBObjectStoreParameters& aOptionalParameters, 1.535 + ErrorResult& aRv) 1.536 +{ 1.537 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.538 + 1.539 + IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction(); 1.540 + 1.541 + if (!transaction || 1.542 + transaction->GetMode() != IDBTransaction::VERSION_CHANGE) { 1.543 + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); 1.544 + return nullptr; 1.545 + } 1.546 + 1.547 + DatabaseInfo* databaseInfo = transaction->DBInfo(); 1.548 + 1.549 + KeyPath keyPath(0); 1.550 + if (NS_FAILED(KeyPath::Parse(aCx, aOptionalParameters.mKeyPath, &keyPath))) { 1.551 + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); 1.552 + return nullptr; 1.553 + } 1.554 + 1.555 + if (databaseInfo->ContainsStoreName(aName)) { 1.556 + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR); 1.557 + return nullptr; 1.558 + } 1.559 + 1.560 + if (!keyPath.IsAllowedForObjectStore(aOptionalParameters.mAutoIncrement)) { 1.561 + aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); 1.562 + return nullptr; 1.563 + } 1.564 + 1.565 + ObjectStoreInfoGuts guts; 1.566 + 1.567 + guts.name = aName; 1.568 + guts.id = databaseInfo->nextObjectStoreId++; 1.569 + guts.keyPath = keyPath; 1.570 + guts.autoIncrement = aOptionalParameters.mAutoIncrement; 1.571 + 1.572 + return CreateObjectStoreInternal(transaction, guts, aRv); 1.573 +} 1.574 + 1.575 +void 1.576 +IDBDatabase::DeleteObjectStore(const nsAString& aName, ErrorResult& aRv) 1.577 +{ 1.578 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.579 + 1.580 + IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction(); 1.581 + 1.582 + if (!transaction || 1.583 + transaction->GetMode() != IDBTransaction::VERSION_CHANGE) { 1.584 + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); 1.585 + return; 1.586 + } 1.587 + 1.588 + DatabaseInfo* info = transaction->DBInfo(); 1.589 + ObjectStoreInfo* objectStoreInfo = info->GetObjectStore(aName); 1.590 + if (!objectStoreInfo) { 1.591 + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR); 1.592 + return; 1.593 + } 1.594 + 1.595 + if (IndexedDatabaseManager::IsMainProcess()) { 1.596 + nsRefPtr<DeleteObjectStoreHelper> helper = 1.597 + new DeleteObjectStoreHelper(transaction, objectStoreInfo->id); 1.598 + 1.599 + nsresult rv = helper->DispatchToTransactionPool(); 1.600 + if (NS_FAILED(rv)) { 1.601 + IDB_WARNING("Failed to dispatch!"); 1.602 + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.603 + return; 1.604 + } 1.605 + } 1.606 + else { 1.607 + IndexedDBTransactionChild* actor = transaction->GetActorChild(); 1.608 + NS_ASSERTION(actor, "Must have an actor here!"); 1.609 + 1.610 + actor->SendDeleteObjectStore(nsString(aName)); 1.611 + } 1.612 + 1.613 + transaction->RemoveObjectStore(aName); 1.614 + 1.615 + IDB_PROFILER_MARK("IndexedDB Pseudo-request: " 1.616 + "database(%s).transaction(%s).deleteObjectStore(\"%s\")", 1.617 + "MT IDBDatabase.deleteObjectStore()", 1.618 + IDB_PROFILER_STRING(this), 1.619 + IDB_PROFILER_STRING(transaction), 1.620 + NS_ConvertUTF16toUTF8(aName).get()); 1.621 +} 1.622 + 1.623 +already_AddRefed<indexedDB::IDBTransaction> 1.624 +IDBDatabase::Transaction(const Sequence<nsString>& aStoreNames, 1.625 + IDBTransactionMode aMode, ErrorResult& aRv) 1.626 +{ 1.627 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.628 + 1.629 + if (QuotaManager::IsShuttingDown()) { 1.630 + IDB_REPORT_INTERNAL_ERR(); 1.631 + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.632 + return nullptr; 1.633 + } 1.634 + 1.635 + if (mClosed) { 1.636 + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); 1.637 + return nullptr; 1.638 + } 1.639 + 1.640 + if (mRunningVersionChange) { 1.641 + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); 1.642 + return nullptr; 1.643 + } 1.644 + 1.645 + if (aStoreNames.IsEmpty()) { 1.646 + aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); 1.647 + return nullptr; 1.648 + } 1.649 + 1.650 + IDBTransaction::Mode transactionMode = IDBTransaction::READ_ONLY; 1.651 + switch (aMode) { 1.652 + case IDBTransactionMode::Readonly: 1.653 + transactionMode = IDBTransaction::READ_ONLY; 1.654 + break; 1.655 + case IDBTransactionMode::Readwrite: 1.656 + transactionMode = IDBTransaction::READ_WRITE; 1.657 + break; 1.658 + case IDBTransactionMode::Versionchange: 1.659 + transactionMode = IDBTransaction::VERSION_CHANGE; 1.660 + break; 1.661 + default: 1.662 + MOZ_CRASH("Unknown mode!"); 1.663 + } 1.664 + 1.665 + // Now check to make sure the object store names we collected actually exist. 1.666 + DatabaseInfo* info = Info(); 1.667 + for (uint32_t index = 0; index < aStoreNames.Length(); index++) { 1.668 + if (!info->ContainsStoreName(aStoreNames[index])) { 1.669 + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR); 1.670 + return nullptr; 1.671 + } 1.672 + } 1.673 + 1.674 + nsRefPtr<IDBTransaction> transaction = 1.675 + IDBTransaction::Create(this, aStoreNames, transactionMode, false); 1.676 + if (!transaction) { 1.677 + IDB_WARNING("Failed to create the transaction!"); 1.678 + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.679 + return nullptr; 1.680 + } 1.681 + 1.682 + IDB_PROFILER_MARK("IndexedDB Transaction %llu: database(%s).transaction(%s)", 1.683 + "IDBTransaction[%llu] MT Started", 1.684 + transaction->GetSerialNumber(), IDB_PROFILER_STRING(this), 1.685 + IDB_PROFILER_STRING(transaction)); 1.686 + 1.687 + return transaction.forget(); 1.688 +} 1.689 + 1.690 +already_AddRefed<IDBRequest> 1.691 +IDBDatabase::MozCreateFileHandle(const nsAString& aName, 1.692 + const Optional<nsAString>& aType, 1.693 + ErrorResult& aRv) 1.694 +{ 1.695 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.696 + 1.697 + if (!IndexedDatabaseManager::IsMainProcess()) { 1.698 + IDB_WARNING("Not supported yet!"); 1.699 + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.700 + return nullptr; 1.701 + } 1.702 + 1.703 + if (QuotaManager::IsShuttingDown()) { 1.704 + IDB_REPORT_INTERNAL_ERR(); 1.705 + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.706 + return nullptr; 1.707 + } 1.708 + 1.709 + if (mClosed) { 1.710 + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); 1.711 + return nullptr; 1.712 + } 1.713 + 1.714 + nsRefPtr<IDBRequest> request = IDBRequest::Create(this, nullptr); 1.715 + 1.716 + nsRefPtr<CreateFileHelper> helper = 1.717 + new CreateFileHelper(this, request, aName, 1.718 + aType.WasPassed() ? aType.Value() : EmptyString()); 1.719 + 1.720 + QuotaManager* quotaManager = QuotaManager::Get(); 1.721 + NS_ASSERTION(quotaManager, "We should definitely have a manager here"); 1.722 + 1.723 + nsresult rv = helper->Dispatch(quotaManager->IOThread()); 1.724 + if (NS_FAILED(rv)) { 1.725 + IDB_WARNING("Failed to dispatch!"); 1.726 + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.727 + return nullptr; 1.728 + } 1.729 + 1.730 + return request.forget(); 1.731 +} 1.732 + 1.733 +NS_IMETHODIMP 1.734 +IDBDatabase::Close() 1.735 +{ 1.736 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.737 + 1.738 + CloseInternal(false); 1.739 + 1.740 + NS_ASSERTION(mClosed, "Should have set the closed flag!"); 1.741 + 1.742 + return NS_OK; 1.743 +} 1.744 + 1.745 +NS_IMETHODIMP_(const nsACString&) 1.746 +IDBDatabase::Id() 1.747 +{ 1.748 + return mDatabaseId; 1.749 +} 1.750 + 1.751 +NS_IMETHODIMP_(bool) 1.752 +IDBDatabase::IsInvalidated() 1.753 +{ 1.754 + return mInvalidated; 1.755 +} 1.756 + 1.757 +NS_IMETHODIMP_(bool) 1.758 +IDBDatabase::IsShuttingDown() 1.759 +{ 1.760 + return QuotaManager::IsShuttingDown(); 1.761 +} 1.762 + 1.763 +NS_IMETHODIMP_(void) 1.764 +IDBDatabase::SetThreadLocals() 1.765 +{ 1.766 + NS_ASSERTION(GetOwner(), "Should have owner!"); 1.767 + QuotaManager::SetCurrentWindow(GetOwner()); 1.768 +} 1.769 + 1.770 +NS_IMETHODIMP_(void) 1.771 +IDBDatabase::UnsetThreadLocals() 1.772 +{ 1.773 + QuotaManager::SetCurrentWindow(nullptr); 1.774 +} 1.775 + 1.776 +NS_IMETHODIMP_(mozilla::dom::quota::Client*) 1.777 +IDBDatabase::GetClient() 1.778 +{ 1.779 + return mQuotaClient; 1.780 +} 1.781 + 1.782 +NS_IMETHODIMP_(bool) 1.783 +IDBDatabase::IsOwned(nsPIDOMWindow* aOwner) 1.784 +{ 1.785 + return GetOwner() == aOwner; 1.786 +} 1.787 + 1.788 +NS_IMETHODIMP_(const nsACString&) 1.789 +IDBDatabase::Origin() 1.790 +{ 1.791 + return mASCIIOrigin; 1.792 +} 1.793 + 1.794 +nsresult 1.795 +IDBDatabase::PostHandleEvent(EventChainPostVisitor& aVisitor) 1.796 +{ 1.797 + return IndexedDatabaseManager::FireWindowOnError(GetOwner(), aVisitor); 1.798 +} 1.799 + 1.800 +AsyncConnectionHelper::ChildProcessSendResult 1.801 +NoRequestDatabaseHelper::SendResponseToChildProcess(nsresult aResultCode) 1.802 +{ 1.803 + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); 1.804 + return Success_NotSent; 1.805 +} 1.806 + 1.807 +nsresult 1.808 +NoRequestDatabaseHelper::UnpackResponseFromParentProcess( 1.809 + const ResponseValue& aResponseValue) 1.810 +{ 1.811 + MOZ_CRASH("Should never get here!"); 1.812 +} 1.813 + 1.814 +nsresult 1.815 +NoRequestDatabaseHelper::OnSuccess() 1.816 +{ 1.817 + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); 1.818 + return NS_OK; 1.819 +} 1.820 + 1.821 +void 1.822 +NoRequestDatabaseHelper::OnError() 1.823 +{ 1.824 + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); 1.825 + mTransaction->Abort(GetResultCode()); 1.826 +} 1.827 + 1.828 +nsresult 1.829 +CreateObjectStoreHelper::DoDatabaseWork(mozIStorageConnection* aConnection) 1.830 +{ 1.831 + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); 1.832 + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); 1.833 + 1.834 + PROFILER_LABEL("IndexedDB", "CreateObjectStoreHelper::DoDatabaseWork"); 1.835 + 1.836 + if (IndexedDatabaseManager::InLowDiskSpaceMode()) { 1.837 + NS_WARNING("Refusing to create additional objectStore because disk space " 1.838 + "is low!"); 1.839 + return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; 1.840 + } 1.841 + 1.842 + nsCOMPtr<mozIStorageStatement> stmt = 1.843 + mTransaction->GetCachedStatement(NS_LITERAL_CSTRING( 1.844 + "INSERT INTO object_store (id, auto_increment, name, key_path) " 1.845 + "VALUES (:id, :auto_increment, :name, :key_path)" 1.846 + )); 1.847 + IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.848 + 1.849 + mozStorageStatementScoper scoper(stmt); 1.850 + 1.851 + nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), 1.852 + mObjectStore->Id()); 1.853 + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.854 + 1.855 + rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("auto_increment"), 1.856 + mObjectStore->IsAutoIncrement() ? 1 : 0); 1.857 + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.858 + 1.859 + rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mObjectStore->Name()); 1.860 + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.861 + 1.862 + const KeyPath& keyPath = mObjectStore->GetKeyPath(); 1.863 + if (keyPath.IsValid()) { 1.864 + nsAutoString keyPathSerialization; 1.865 + keyPath.SerializeToString(keyPathSerialization); 1.866 + rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key_path"), 1.867 + keyPathSerialization); 1.868 + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.869 + } 1.870 + else { 1.871 + rv = stmt->BindNullByName(NS_LITERAL_CSTRING("key_path")); 1.872 + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.873 + } 1.874 + 1.875 + rv = stmt->Execute(); 1.876 + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.877 + 1.878 + return NS_OK; 1.879 +} 1.880 + 1.881 +void 1.882 +CreateObjectStoreHelper::ReleaseMainThreadObjects() 1.883 +{ 1.884 + mObjectStore = nullptr; 1.885 + NoRequestDatabaseHelper::ReleaseMainThreadObjects(); 1.886 +} 1.887 + 1.888 +nsresult 1.889 +DeleteObjectStoreHelper::DoDatabaseWork(mozIStorageConnection* aConnection) 1.890 +{ 1.891 + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); 1.892 + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); 1.893 + 1.894 + PROFILER_LABEL("IndexedDB", "DeleteObjectStoreHelper::DoDatabaseWork"); 1.895 + 1.896 + nsCOMPtr<mozIStorageStatement> stmt = 1.897 + mTransaction->GetCachedStatement(NS_LITERAL_CSTRING( 1.898 + "DELETE FROM object_store " 1.899 + "WHERE id = :id " 1.900 + )); 1.901 + IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.902 + 1.903 + mozStorageStatementScoper scoper(stmt); 1.904 + 1.905 + nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mObjectStoreId); 1.906 + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.907 + 1.908 + rv = stmt->Execute(); 1.909 + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.910 + 1.911 + return NS_OK; 1.912 +} 1.913 + 1.914 +nsresult 1.915 +CreateFileHelper::DoDatabaseWork(mozIStorageConnection* aConnection) 1.916 +{ 1.917 + AssertIsOnIOThread(); 1.918 + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); 1.919 + 1.920 + PROFILER_LABEL("IndexedDB", "CreateFileHelper::DoDatabaseWork"); 1.921 + 1.922 + if (IndexedDatabaseManager::InLowDiskSpaceMode()) { 1.923 + NS_WARNING("Refusing to create file because disk space is low!"); 1.924 + return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; 1.925 + } 1.926 + 1.927 + FileManager* fileManager = mDatabase->Manager(); 1.928 + 1.929 + mFileInfo = fileManager->GetNewFileInfo(); 1.930 + IDB_ENSURE_TRUE(mFileInfo, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.931 + 1.932 + const int64_t& fileId = mFileInfo->Id(); 1.933 + 1.934 + nsCOMPtr<nsIFile> directory = fileManager->EnsureJournalDirectory(); 1.935 + NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE); 1.936 + 1.937 + nsCOMPtr<nsIFile> file = fileManager->GetFileForId(directory, fileId); 1.938 + NS_ENSURE_TRUE(file, NS_ERROR_FAILURE); 1.939 + 1.940 + nsresult rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0644); 1.941 + NS_ENSURE_SUCCESS(rv, rv); 1.942 + 1.943 + directory = fileManager->GetDirectory(); 1.944 + IDB_ENSURE_TRUE(directory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.945 + 1.946 + file = fileManager->GetFileForId(directory, fileId); 1.947 + IDB_ENSURE_TRUE(file, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.948 + 1.949 + rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0644); 1.950 + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.951 + 1.952 + return NS_OK; 1.953 +} 1.954 + 1.955 +nsresult 1.956 +CreateFileHelper::GetSuccessResult(JSContext* aCx, 1.957 + JS::MutableHandle<JS::Value> aVal) 1.958 +{ 1.959 + nsRefPtr<IDBFileHandle> fileHandle = 1.960 + IDBFileHandle::Create(mDatabase, mName, mType, mFileInfo.forget()); 1.961 + IDB_ENSURE_TRUE(fileHandle, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.962 + 1.963 + return WrapNative(aCx, NS_ISUPPORTS_CAST(EventTarget*, fileHandle), aVal); 1.964 +}