1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/indexedDB/IDBFactory.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,835 @@ 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 "IDBFactory.h" 1.11 + 1.12 +#include "nsIFile.h" 1.13 +#include "nsIPrincipal.h" 1.14 +#include "nsIScriptContext.h" 1.15 +#include "nsIScriptSecurityManager.h" 1.16 +#include "nsIXPConnect.h" 1.17 +#include "nsIXPCScriptable.h" 1.18 + 1.19 +#include <algorithm> 1.20 +#include "mozilla/dom/ContentParent.h" 1.21 +#include "mozilla/dom/ContentChild.h" 1.22 +#include "mozilla/dom/IDBFactoryBinding.h" 1.23 +#include "mozilla/dom/PBrowserChild.h" 1.24 +#include "mozilla/dom/quota/OriginOrPatternString.h" 1.25 +#include "mozilla/dom/quota/QuotaManager.h" 1.26 +#include "mozilla/dom/TabChild.h" 1.27 +#include "mozilla/Preferences.h" 1.28 +#include "mozilla/storage.h" 1.29 +#include "nsComponentManagerUtils.h" 1.30 +#include "nsCharSeparatedTokenizer.h" 1.31 +#include "nsContentUtils.h" 1.32 +#include "nsCxPusher.h" 1.33 +#include "nsDOMClassInfoID.h" 1.34 +#include "nsGlobalWindow.h" 1.35 +#include "nsHashKeys.h" 1.36 +#include "nsPIDOMWindow.h" 1.37 +#include "nsServiceManagerUtils.h" 1.38 +#include "nsThreadUtils.h" 1.39 +#include "nsXPCOMCID.h" 1.40 + 1.41 +#include "AsyncConnectionHelper.h" 1.42 +#include "CheckPermissionsHelper.h" 1.43 +#include "DatabaseInfo.h" 1.44 +#include "IDBDatabase.h" 1.45 +#include "IDBEvents.h" 1.46 +#include "IDBKeyRange.h" 1.47 +#include "IndexedDatabaseManager.h" 1.48 +#include "Key.h" 1.49 +#include "ProfilerHelpers.h" 1.50 +#include "ReportInternalError.h" 1.51 +#include "nsNetUtil.h" 1.52 + 1.53 +#include "ipc/IndexedDBChild.h" 1.54 + 1.55 +#define PREF_INDEXEDDB_ENABLED "dom.indexedDB.enabled" 1.56 + 1.57 +USING_INDEXEDDB_NAMESPACE 1.58 +USING_QUOTA_NAMESPACE 1.59 + 1.60 +using mozilla::dom::ContentChild; 1.61 +using mozilla::dom::ContentParent; 1.62 +using mozilla::dom::IDBOpenDBOptions; 1.63 +using mozilla::dom::NonNull; 1.64 +using mozilla::dom::Optional; 1.65 +using mozilla::dom::TabChild; 1.66 +using mozilla::ErrorResult; 1.67 +using mozilla::Preferences; 1.68 + 1.69 +namespace { 1.70 + 1.71 +struct ObjectStoreInfoMap 1.72 +{ 1.73 + ObjectStoreInfoMap() 1.74 + : id(INT64_MIN), info(nullptr) { } 1.75 + 1.76 + int64_t id; 1.77 + ObjectStoreInfo* info; 1.78 +}; 1.79 + 1.80 +} // anonymous namespace 1.81 + 1.82 +IDBFactory::IDBFactory() 1.83 +: mPrivilege(Content), mDefaultPersistenceType(PERSISTENCE_TYPE_TEMPORARY), 1.84 + mOwningObject(nullptr), mActorChild(nullptr), mActorParent(nullptr), 1.85 + mContentParent(nullptr), mRootedOwningObject(false) 1.86 +{ 1.87 + SetIsDOMBinding(); 1.88 +} 1.89 + 1.90 +IDBFactory::~IDBFactory() 1.91 +{ 1.92 + NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!"); 1.93 + if (mActorChild) { 1.94 + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); 1.95 + mActorChild->Send__delete__(mActorChild); 1.96 + NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!"); 1.97 + } 1.98 + if (mRootedOwningObject) { 1.99 + mOwningObject = nullptr; 1.100 + mozilla::DropJSObjects(this); 1.101 + } 1.102 +} 1.103 + 1.104 +// static 1.105 +nsresult 1.106 +IDBFactory::Create(nsPIDOMWindow* aWindow, 1.107 + const nsACString& aGroup, 1.108 + const nsACString& aASCIIOrigin, 1.109 + ContentParent* aContentParent, 1.110 + IDBFactory** aFactory) 1.111 +{ 1.112 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.113 + NS_ASSERTION(aASCIIOrigin.IsEmpty() || nsContentUtils::IsCallerChrome(), 1.114 + "Non-chrome may not supply their own origin!"); 1.115 + 1.116 + IDB_ENSURE_TRUE(aWindow, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.117 + 1.118 + if (aWindow->IsOuterWindow()) { 1.119 + aWindow = aWindow->GetCurrentInnerWindow(); 1.120 + IDB_ENSURE_TRUE(aWindow, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.121 + } 1.122 + 1.123 + // Make sure that the manager is up before we do anything here since lots of 1.124 + // decisions depend on which process we're running in. 1.125 + indexedDB::IndexedDatabaseManager* mgr = 1.126 + indexedDB::IndexedDatabaseManager::GetOrCreate(); 1.127 + IDB_ENSURE_TRUE(mgr, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.128 + 1.129 + nsresult rv; 1.130 + 1.131 + nsCString group(aGroup); 1.132 + nsCString origin(aASCIIOrigin); 1.133 + StoragePrivilege privilege; 1.134 + PersistenceType defaultPersistenceType; 1.135 + if (origin.IsEmpty()) { 1.136 + NS_ASSERTION(aGroup.IsEmpty(), "Should be empty too!"); 1.137 + 1.138 + rv = QuotaManager::GetInfoFromWindow(aWindow, &group, &origin, &privilege, 1.139 + &defaultPersistenceType); 1.140 + } 1.141 + else { 1.142 + rv = QuotaManager::GetInfoFromWindow(aWindow, nullptr, nullptr, &privilege, 1.143 + &defaultPersistenceType); 1.144 + } 1.145 + if (NS_FAILED(rv)) { 1.146 + // Not allowed. 1.147 + *aFactory = nullptr; 1.148 + return NS_OK; 1.149 + } 1.150 + 1.151 + nsRefPtr<IDBFactory> factory = new IDBFactory(); 1.152 + factory->mGroup = group; 1.153 + factory->mASCIIOrigin = origin; 1.154 + factory->mPrivilege = privilege; 1.155 + factory->mDefaultPersistenceType = defaultPersistenceType; 1.156 + factory->mWindow = aWindow; 1.157 + factory->mContentParent = aContentParent; 1.158 + 1.159 + if (!IndexedDatabaseManager::IsMainProcess()) { 1.160 + TabChild* tabChild = TabChild::GetFrom(aWindow); 1.161 + IDB_ENSURE_TRUE(tabChild, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.162 + 1.163 + IndexedDBChild* actor = new IndexedDBChild(origin); 1.164 + 1.165 + bool allowed; 1.166 + tabChild->SendPIndexedDBConstructor(actor, group, origin, &allowed); 1.167 + 1.168 + if (!allowed) { 1.169 + actor->Send__delete__(actor); 1.170 + *aFactory = nullptr; 1.171 + return NS_OK; 1.172 + } 1.173 + 1.174 + actor->SetFactory(factory); 1.175 + } 1.176 + 1.177 + factory.forget(aFactory); 1.178 + return NS_OK; 1.179 +} 1.180 + 1.181 +// static 1.182 +nsresult 1.183 +IDBFactory::Create(JSContext* aCx, 1.184 + JS::Handle<JSObject*> aOwningObject, 1.185 + ContentParent* aContentParent, 1.186 + IDBFactory** aFactory) 1.187 +{ 1.188 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.189 + NS_ASSERTION(aCx, "Null context!"); 1.190 + NS_ASSERTION(aOwningObject, "Null object!"); 1.191 + NS_ASSERTION(JS_GetGlobalForObject(aCx, aOwningObject) == aOwningObject, 1.192 + "Not a global object!"); 1.193 + NS_ASSERTION(nsContentUtils::IsCallerChrome(), "Only for chrome!"); 1.194 + 1.195 + // Make sure that the manager is up before we do anything here since lots of 1.196 + // decisions depend on which process we're running in. 1.197 + IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetOrCreate(); 1.198 + IDB_ENSURE_TRUE(mgr, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.199 + 1.200 + nsCString group; 1.201 + nsCString origin; 1.202 + StoragePrivilege privilege; 1.203 + PersistenceType defaultPersistenceType; 1.204 + QuotaManager::GetInfoForChrome(&group, &origin, &privilege, 1.205 + &defaultPersistenceType); 1.206 + 1.207 + nsRefPtr<IDBFactory> factory = new IDBFactory(); 1.208 + factory->mGroup = group; 1.209 + factory->mASCIIOrigin = origin; 1.210 + factory->mPrivilege = privilege; 1.211 + factory->mDefaultPersistenceType = defaultPersistenceType; 1.212 + factory->mOwningObject = aOwningObject; 1.213 + factory->mContentParent = aContentParent; 1.214 + 1.215 + mozilla::HoldJSObjects(factory.get()); 1.216 + factory->mRootedOwningObject = true; 1.217 + 1.218 + if (!IndexedDatabaseManager::IsMainProcess()) { 1.219 + ContentChild* contentChild = ContentChild::GetSingleton(); 1.220 + IDB_ENSURE_TRUE(contentChild, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.221 + 1.222 + IndexedDBChild* actor = new IndexedDBChild(origin); 1.223 + 1.224 + contentChild->SendPIndexedDBConstructor(actor); 1.225 + 1.226 + actor->SetFactory(factory); 1.227 + } 1.228 + 1.229 + factory.forget(aFactory); 1.230 + return NS_OK; 1.231 +} 1.232 + 1.233 +// static 1.234 +nsresult 1.235 +IDBFactory::Create(ContentParent* aContentParent, 1.236 + IDBFactory** aFactory) 1.237 +{ 1.238 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.239 + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); 1.240 + NS_ASSERTION(nsContentUtils::IsCallerChrome(), "Only for chrome!"); 1.241 + NS_ASSERTION(aContentParent, "Null ContentParent!"); 1.242 + 1.243 + NS_ASSERTION(!nsContentUtils::GetCurrentJSContext(), "Should be called from C++"); 1.244 + 1.245 + // We need to get this information before we push a null principal to avoid 1.246 + // IsCallerChrome() assertion in quota manager. 1.247 + nsCString group; 1.248 + nsCString origin; 1.249 + StoragePrivilege privilege; 1.250 + PersistenceType defaultPersistenceType; 1.251 + QuotaManager::GetInfoForChrome(&group, &origin, &privilege, 1.252 + &defaultPersistenceType); 1.253 + 1.254 + nsCOMPtr<nsIPrincipal> principal = 1.255 + do_CreateInstance("@mozilla.org/nullprincipal;1"); 1.256 + NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE); 1.257 + 1.258 + AutoSafeJSContext cx; 1.259 + 1.260 + nsIXPConnect* xpc = nsContentUtils::XPConnect(); 1.261 + NS_ASSERTION(xpc, "This should never be null!"); 1.262 + 1.263 + nsCOMPtr<nsIXPConnectJSObjectHolder> globalHolder; 1.264 + nsresult rv = xpc->CreateSandbox(cx, principal, getter_AddRefs(globalHolder)); 1.265 + NS_ENSURE_SUCCESS(rv, rv); 1.266 + 1.267 + JS::Rooted<JSObject*> global(cx, globalHolder->GetJSObject()); 1.268 + NS_ENSURE_STATE(global); 1.269 + 1.270 + // The CreateSandbox call returns a proxy to the actual sandbox object. We 1.271 + // don't need a proxy here. 1.272 + global = js::UncheckedUnwrap(global); 1.273 + 1.274 + JSAutoCompartment ac(cx, global); 1.275 + 1.276 + nsRefPtr<IDBFactory> factory = new IDBFactory(); 1.277 + factory->mGroup = group; 1.278 + factory->mASCIIOrigin = origin; 1.279 + factory->mPrivilege = privilege; 1.280 + factory->mDefaultPersistenceType = defaultPersistenceType; 1.281 + factory->mOwningObject = global; 1.282 + factory->mContentParent = aContentParent; 1.283 + 1.284 + mozilla::HoldJSObjects(factory.get()); 1.285 + factory->mRootedOwningObject = true; 1.286 + 1.287 + factory.forget(aFactory); 1.288 + return NS_OK; 1.289 +} 1.290 + 1.291 +// static 1.292 +already_AddRefed<nsIFileURL> 1.293 +IDBFactory::GetDatabaseFileURL(nsIFile* aDatabaseFile, 1.294 + PersistenceType aPersistenceType, 1.295 + const nsACString& aGroup, 1.296 + const nsACString& aOrigin) 1.297 +{ 1.298 + nsCOMPtr<nsIURI> uri; 1.299 + nsresult rv = NS_NewFileURI(getter_AddRefs(uri), aDatabaseFile); 1.300 + NS_ENSURE_SUCCESS(rv, nullptr); 1.301 + 1.302 + nsCOMPtr<nsIFileURL> fileUrl = do_QueryInterface(uri); 1.303 + NS_ASSERTION(fileUrl, "This should always succeed!"); 1.304 + 1.305 + nsAutoCString type; 1.306 + PersistenceTypeToText(aPersistenceType, type); 1.307 + 1.308 + rv = fileUrl->SetQuery(NS_LITERAL_CSTRING("persistenceType=") + type + 1.309 + NS_LITERAL_CSTRING("&group=") + aGroup + 1.310 + NS_LITERAL_CSTRING("&origin=") + aOrigin); 1.311 + NS_ENSURE_SUCCESS(rv, nullptr); 1.312 + 1.313 + return fileUrl.forget(); 1.314 +} 1.315 + 1.316 +// static 1.317 +already_AddRefed<mozIStorageConnection> 1.318 +IDBFactory::GetConnection(const nsAString& aDatabaseFilePath, 1.319 + PersistenceType aPersistenceType, 1.320 + const nsACString& aGroup, 1.321 + const nsACString& aOrigin) 1.322 +{ 1.323 + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); 1.324 + NS_ASSERTION(StringEndsWith(aDatabaseFilePath, NS_LITERAL_STRING(".sqlite")), 1.325 + "Bad file path!"); 1.326 + 1.327 + nsCOMPtr<nsIFile> dbFile(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID)); 1.328 + NS_ENSURE_TRUE(dbFile, nullptr); 1.329 + 1.330 + nsresult rv = dbFile->InitWithPath(aDatabaseFilePath); 1.331 + NS_ENSURE_SUCCESS(rv, nullptr); 1.332 + 1.333 + bool exists; 1.334 + rv = dbFile->Exists(&exists); 1.335 + NS_ENSURE_SUCCESS(rv, nullptr); 1.336 + NS_ENSURE_TRUE(exists, nullptr); 1.337 + 1.338 + nsCOMPtr<nsIFileURL> dbFileUrl = 1.339 + GetDatabaseFileURL(dbFile, aPersistenceType, aGroup, aOrigin); 1.340 + NS_ENSURE_TRUE(dbFileUrl, nullptr); 1.341 + 1.342 + nsCOMPtr<mozIStorageService> ss = 1.343 + do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID); 1.344 + NS_ENSURE_TRUE(ss, nullptr); 1.345 + 1.346 + nsCOMPtr<mozIStorageConnection> connection; 1.347 + rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection)); 1.348 + NS_ENSURE_SUCCESS(rv, nullptr); 1.349 + 1.350 + rv = SetDefaultPragmas(connection); 1.351 + NS_ENSURE_SUCCESS(rv, nullptr); 1.352 + 1.353 + return connection.forget(); 1.354 +} 1.355 + 1.356 +// static 1.357 +nsresult 1.358 +IDBFactory::SetDefaultPragmas(mozIStorageConnection* aConnection) 1.359 +{ 1.360 + NS_ASSERTION(aConnection, "Null connection!"); 1.361 + 1.362 + static const char query[] = 1.363 +#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK) 1.364 + // Switch the journaling mode to TRUNCATE to avoid changing the directory 1.365 + // structure at the conclusion of every transaction for devices with slower 1.366 + // file systems. 1.367 + "PRAGMA journal_mode = TRUNCATE; " 1.368 +#endif 1.369 + // We use foreign keys in lots of places. 1.370 + "PRAGMA foreign_keys = ON; " 1.371 + // The "INSERT OR REPLACE" statement doesn't fire the update trigger, 1.372 + // instead it fires only the insert trigger. This confuses the update 1.373 + // refcount function. This behavior changes with enabled recursive triggers, 1.374 + // so the statement fires the delete trigger first and then the insert 1.375 + // trigger. 1.376 + "PRAGMA recursive_triggers = ON;"; 1.377 + 1.378 + nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(query)); 1.379 + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.380 + 1.381 + return NS_OK; 1.382 +} 1.383 + 1.384 +inline 1.385 +bool 1.386 +IgnoreWhitespace(char16_t c) 1.387 +{ 1.388 + return false; 1.389 +} 1.390 + 1.391 +// static 1.392 +nsresult 1.393 +IDBFactory::LoadDatabaseInformation(mozIStorageConnection* aConnection, 1.394 + const nsACString& aDatabaseId, 1.395 + uint64_t* aVersion, 1.396 + ObjectStoreInfoArray& aObjectStores) 1.397 +{ 1.398 + AssertIsOnIOThread(); 1.399 + NS_ASSERTION(aConnection, "Null pointer!"); 1.400 + 1.401 + aObjectStores.Clear(); 1.402 + 1.403 + // Load object store names and ids. 1.404 + nsCOMPtr<mozIStorageStatement> stmt; 1.405 + nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( 1.406 + "SELECT name, id, key_path, auto_increment " 1.407 + "FROM object_store" 1.408 + ), getter_AddRefs(stmt)); 1.409 + NS_ENSURE_SUCCESS(rv, rv); 1.410 + 1.411 + nsAutoTArray<ObjectStoreInfoMap, 20> infoMap; 1.412 + 1.413 + bool hasResult; 1.414 + while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { 1.415 + nsRefPtr<ObjectStoreInfo>* element = 1.416 + aObjectStores.AppendElement(new ObjectStoreInfo()); 1.417 + 1.418 + ObjectStoreInfo* info = element->get(); 1.419 + 1.420 + rv = stmt->GetString(0, info->name); 1.421 + NS_ENSURE_SUCCESS(rv, rv); 1.422 + 1.423 + info->id = stmt->AsInt64(1); 1.424 + 1.425 + int32_t columnType; 1.426 + nsresult rv = stmt->GetTypeOfIndex(2, &columnType); 1.427 + NS_ENSURE_SUCCESS(rv, rv); 1.428 + 1.429 + // NB: We don't have to handle the NULL case, since that is the default 1.430 + // for a new KeyPath. 1.431 + if (columnType != mozIStorageStatement::VALUE_TYPE_NULL) { 1.432 + NS_ASSERTION(columnType == mozIStorageStatement::VALUE_TYPE_TEXT, 1.433 + "Should be a string"); 1.434 + nsString keyPathSerialization; 1.435 + rv = stmt->GetString(2, keyPathSerialization); 1.436 + NS_ENSURE_SUCCESS(rv, rv); 1.437 + 1.438 + info->keyPath = KeyPath::DeserializeFromString(keyPathSerialization); 1.439 + } 1.440 + 1.441 + info->nextAutoIncrementId = stmt->AsInt64(3); 1.442 + info->comittedAutoIncrementId = info->nextAutoIncrementId; 1.443 + 1.444 + info->autoIncrement = !!info->nextAutoIncrementId; 1.445 + 1.446 + ObjectStoreInfoMap* mapEntry = infoMap.AppendElement(); 1.447 + NS_ENSURE_TRUE(mapEntry, NS_ERROR_OUT_OF_MEMORY); 1.448 + 1.449 + mapEntry->id = info->id; 1.450 + mapEntry->info = info; 1.451 + } 1.452 + NS_ENSURE_SUCCESS(rv, rv); 1.453 + 1.454 + // Load index information 1.455 + rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( 1.456 + "SELECT object_store_id, id, name, key_path, unique_index, multientry " 1.457 + "FROM object_store_index" 1.458 + ), getter_AddRefs(stmt)); 1.459 + NS_ENSURE_SUCCESS(rv, rv); 1.460 + 1.461 + while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { 1.462 + int64_t objectStoreId = stmt->AsInt64(0); 1.463 + 1.464 + ObjectStoreInfo* objectStoreInfo = nullptr; 1.465 + for (uint32_t index = 0; index < infoMap.Length(); index++) { 1.466 + if (infoMap[index].id == objectStoreId) { 1.467 + objectStoreInfo = infoMap[index].info; 1.468 + break; 1.469 + } 1.470 + } 1.471 + 1.472 + if (!objectStoreInfo) { 1.473 + NS_ERROR("Index for nonexistant object store!"); 1.474 + return NS_ERROR_UNEXPECTED; 1.475 + } 1.476 + 1.477 + IndexInfo* indexInfo = objectStoreInfo->indexes.AppendElement(); 1.478 + NS_ENSURE_TRUE(indexInfo, NS_ERROR_OUT_OF_MEMORY); 1.479 + 1.480 + indexInfo->id = stmt->AsInt64(1); 1.481 + 1.482 + rv = stmt->GetString(2, indexInfo->name); 1.483 + NS_ENSURE_SUCCESS(rv, rv); 1.484 + 1.485 + nsString keyPathSerialization; 1.486 + rv = stmt->GetString(3, keyPathSerialization); 1.487 + NS_ENSURE_SUCCESS(rv, rv); 1.488 + 1.489 + // XXX bent wants to assert here 1.490 + indexInfo->keyPath = KeyPath::DeserializeFromString(keyPathSerialization); 1.491 + indexInfo->unique = !!stmt->AsInt32(4); 1.492 + indexInfo->multiEntry = !!stmt->AsInt32(5); 1.493 + } 1.494 + NS_ENSURE_SUCCESS(rv, rv); 1.495 + 1.496 + // Load version information. 1.497 + rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( 1.498 + "SELECT version " 1.499 + "FROM database" 1.500 + ), getter_AddRefs(stmt)); 1.501 + NS_ENSURE_SUCCESS(rv, rv); 1.502 + 1.503 + rv = stmt->ExecuteStep(&hasResult); 1.504 + NS_ENSURE_SUCCESS(rv, rv); 1.505 + 1.506 + if (!hasResult) { 1.507 + NS_ERROR("Database has no version!"); 1.508 + return NS_ERROR_UNEXPECTED; 1.509 + } 1.510 + 1.511 + int64_t version = 0; 1.512 + rv = stmt->GetInt64(0, &version); 1.513 + 1.514 + *aVersion = std::max<int64_t>(version, 0); 1.515 + 1.516 + return rv; 1.517 +} 1.518 + 1.519 +// static 1.520 +nsresult 1.521 +IDBFactory::SetDatabaseMetadata(DatabaseInfo* aDatabaseInfo, 1.522 + uint64_t aVersion, 1.523 + ObjectStoreInfoArray& aObjectStores) 1.524 +{ 1.525 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.526 + NS_ASSERTION(aDatabaseInfo, "Null pointer!"); 1.527 + 1.528 + ObjectStoreInfoArray objectStores; 1.529 + objectStores.SwapElements(aObjectStores); 1.530 + 1.531 +#ifdef DEBUG 1.532 + { 1.533 + nsTArray<nsString> existingNames; 1.534 + aDatabaseInfo->GetObjectStoreNames(existingNames); 1.535 + NS_ASSERTION(existingNames.IsEmpty(), "Should be an empty DatabaseInfo"); 1.536 + } 1.537 +#endif 1.538 + 1.539 + aDatabaseInfo->version = aVersion; 1.540 + 1.541 + for (uint32_t index = 0; index < objectStores.Length(); index++) { 1.542 + nsRefPtr<ObjectStoreInfo>& info = objectStores[index]; 1.543 + 1.544 + if (!aDatabaseInfo->PutObjectStore(info)) { 1.545 + NS_WARNING("Out of memory!"); 1.546 + return NS_ERROR_OUT_OF_MEMORY; 1.547 + } 1.548 + } 1.549 + 1.550 + return NS_OK; 1.551 +} 1.552 + 1.553 +NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBFactory) 1.554 +NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBFactory) 1.555 + 1.556 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBFactory) 1.557 + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 1.558 + NS_INTERFACE_MAP_ENTRY(nsISupports) 1.559 +NS_INTERFACE_MAP_END 1.560 + 1.561 +NS_IMPL_CYCLE_COLLECTION_CLASS(IDBFactory) 1.562 + 1.563 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBFactory) 1.564 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS 1.565 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow) 1.566 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.567 + 1.568 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBFactory) 1.569 + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 1.570 + if (tmp->mOwningObject) { 1.571 + tmp->mOwningObject = nullptr; 1.572 + } 1.573 + if (tmp->mRootedOwningObject) { 1.574 + mozilla::DropJSObjects(tmp); 1.575 + tmp->mRootedOwningObject = false; 1.576 + } 1.577 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow) 1.578 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.579 + 1.580 +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBFactory) 1.581 + NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER 1.582 + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mOwningObject) 1.583 +NS_IMPL_CYCLE_COLLECTION_TRACE_END 1.584 + 1.585 +nsresult 1.586 +IDBFactory::OpenInternal(const nsAString& aName, 1.587 + int64_t aVersion, 1.588 + PersistenceType aPersistenceType, 1.589 + const nsACString& aGroup, 1.590 + const nsACString& aASCIIOrigin, 1.591 + StoragePrivilege aPrivilege, 1.592 + bool aDeleting, 1.593 + IDBOpenDBRequest** _retval) 1.594 +{ 1.595 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.596 + NS_ASSERTION(mWindow || mOwningObject, "Must have one of these!"); 1.597 + 1.598 + AutoJSContext cx; 1.599 + nsCOMPtr<nsPIDOMWindow> window; 1.600 + JS::Rooted<JSObject*> scriptOwner(cx); 1.601 + 1.602 + if (mWindow) { 1.603 + window = mWindow; 1.604 + scriptOwner = 1.605 + static_cast<nsGlobalWindow*>(window.get())->FastGetGlobalJSObject(); 1.606 + } 1.607 + else { 1.608 + scriptOwner = mOwningObject; 1.609 + } 1.610 + 1.611 + if (aPrivilege == Chrome) { 1.612 + // Chrome privilege, ignore the persistence type parameter. 1.613 + aPersistenceType = PERSISTENCE_TYPE_PERSISTENT; 1.614 + } 1.615 + 1.616 + nsRefPtr<IDBOpenDBRequest> request = 1.617 + IDBOpenDBRequest::Create(this, window, scriptOwner); 1.618 + IDB_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.619 + 1.620 + nsresult rv; 1.621 + 1.622 + if (IndexedDatabaseManager::IsMainProcess()) { 1.623 + nsRefPtr<OpenDatabaseHelper> openHelper = 1.624 + new OpenDatabaseHelper(request, aName, aGroup, aASCIIOrigin, aVersion, 1.625 + aPersistenceType, aDeleting, mContentParent, 1.626 + aPrivilege); 1.627 + 1.628 + rv = openHelper->Init(); 1.629 + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.630 + 1.631 + if (!Preferences::GetBool(PREF_INDEXEDDB_ENABLED)) { 1.632 + openHelper->SetError(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); 1.633 + rv = openHelper->WaitForOpenAllowed(); 1.634 + } 1.635 + else { 1.636 + if (mPrivilege != Chrome && 1.637 + aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) { 1.638 + nsRefPtr<CheckPermissionsHelper> permissionHelper = 1.639 + new CheckPermissionsHelper(openHelper, window); 1.640 + 1.641 + QuotaManager* quotaManager = QuotaManager::Get(); 1.642 + NS_ASSERTION(quotaManager, "This should never be null!"); 1.643 + 1.644 + rv = quotaManager-> 1.645 + WaitForOpenAllowed(OriginOrPatternString::FromOrigin(aASCIIOrigin), 1.646 + Nullable<PersistenceType>(aPersistenceType), 1.647 + openHelper->Id(), permissionHelper); 1.648 + } 1.649 + else { 1.650 + // Chrome and temporary storage doesn't need to check the permission. 1.651 + rv = openHelper->WaitForOpenAllowed(); 1.652 + } 1.653 + } 1.654 + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.655 + } 1.656 + else if (aDeleting) { 1.657 + nsCString databaseId; 1.658 + QuotaManager::GetStorageId(aPersistenceType, aASCIIOrigin, Client::IDB, 1.659 + aName, databaseId); 1.660 + MOZ_ASSERT(!databaseId.IsEmpty()); 1.661 + 1.662 + IndexedDBDeleteDatabaseRequestChild* actor = 1.663 + new IndexedDBDeleteDatabaseRequestChild(this, request, databaseId); 1.664 + 1.665 + mActorChild->SendPIndexedDBDeleteDatabaseRequestConstructor( 1.666 + actor, 1.667 + nsString(aName), 1.668 + aPersistenceType); 1.669 + } 1.670 + else { 1.671 + IndexedDBDatabaseChild* dbActor = 1.672 + static_cast<IndexedDBDatabaseChild*>( 1.673 + mActorChild->SendPIndexedDBDatabaseConstructor(nsString(aName), 1.674 + aVersion, 1.675 + aPersistenceType)); 1.676 + 1.677 + dbActor->SetRequest(request); 1.678 + } 1.679 + 1.680 +#ifdef IDB_PROFILER_USE_MARKS 1.681 + { 1.682 + NS_ConvertUTF16toUTF8 profilerName(aName); 1.683 + if (aDeleting) { 1.684 + IDB_PROFILER_MARK("IndexedDB Request %llu: deleteDatabase(\"%s\")", 1.685 + "MT IDBFactory.deleteDatabase()", 1.686 + request->GetSerialNumber(), profilerName.get()); 1.687 + } 1.688 + else { 1.689 + IDB_PROFILER_MARK("IndexedDB Request %llu: open(\"%s\", %lld)", 1.690 + "MT IDBFactory.open()", 1.691 + request->GetSerialNumber(), profilerName.get(), 1.692 + aVersion); 1.693 + } 1.694 + } 1.695 +#endif 1.696 + 1.697 + request.forget(_retval); 1.698 + return NS_OK; 1.699 +} 1.700 + 1.701 +JSObject* 1.702 +IDBFactory::WrapObject(JSContext* aCx) 1.703 +{ 1.704 + return IDBFactoryBinding::Wrap(aCx, this); 1.705 +} 1.706 + 1.707 +already_AddRefed<IDBOpenDBRequest> 1.708 +IDBFactory::Open(const nsAString& aName, const IDBOpenDBOptions& aOptions, 1.709 + ErrorResult& aRv) 1.710 +{ 1.711 + return Open(nullptr, aName, aOptions.mVersion, aOptions.mStorage, false, aRv); 1.712 +} 1.713 + 1.714 +already_AddRefed<IDBOpenDBRequest> 1.715 +IDBFactory::DeleteDatabase(const nsAString& aName, 1.716 + const IDBOpenDBOptions& aOptions, 1.717 + ErrorResult& aRv) 1.718 +{ 1.719 + return Open(nullptr, aName, Optional<uint64_t>(), aOptions.mStorage, true, 1.720 + aRv); 1.721 +} 1.722 + 1.723 +int16_t 1.724 +IDBFactory::Cmp(JSContext* aCx, JS::Handle<JS::Value> aFirst, 1.725 + JS::Handle<JS::Value> aSecond, ErrorResult& aRv) 1.726 +{ 1.727 + Key first, second; 1.728 + nsresult rv = first.SetFromJSVal(aCx, aFirst); 1.729 + if (NS_FAILED(rv)) { 1.730 + aRv.Throw(rv); 1.731 + return 0; 1.732 + } 1.733 + 1.734 + rv = second.SetFromJSVal(aCx, aSecond); 1.735 + if (NS_FAILED(rv)) { 1.736 + aRv.Throw(rv); 1.737 + return 0; 1.738 + } 1.739 + 1.740 + if (first.IsUnset() || second.IsUnset()) { 1.741 + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); 1.742 + return 0; 1.743 + } 1.744 + 1.745 + return Key::CompareKeys(first, second); 1.746 +} 1.747 + 1.748 +already_AddRefed<IDBOpenDBRequest> 1.749 +IDBFactory::OpenForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName, 1.750 + uint64_t aVersion, ErrorResult& aRv) 1.751 +{ 1.752 + // Just to be on the extra-safe side 1.753 + if (!nsContentUtils::IsCallerChrome()) { 1.754 + MOZ_CRASH(); 1.755 + } 1.756 + 1.757 + return Open(aPrincipal, aName, Optional<uint64_t>(aVersion), 1.758 + Optional<mozilla::dom::StorageType>(), false, aRv); 1.759 +} 1.760 + 1.761 +already_AddRefed<IDBOpenDBRequest> 1.762 +IDBFactory::OpenForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName, 1.763 + const IDBOpenDBOptions& aOptions, ErrorResult& aRv) 1.764 +{ 1.765 + // Just to be on the extra-safe side 1.766 + if (!nsContentUtils::IsCallerChrome()) { 1.767 + MOZ_CRASH(); 1.768 + } 1.769 + 1.770 + return Open(aPrincipal, aName, aOptions.mVersion, aOptions.mStorage, false, 1.771 + aRv); 1.772 +} 1.773 + 1.774 +already_AddRefed<IDBOpenDBRequest> 1.775 +IDBFactory::DeleteForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName, 1.776 + const IDBOpenDBOptions& aOptions, 1.777 + ErrorResult& aRv) 1.778 +{ 1.779 + // Just to be on the extra-safe side 1.780 + if (!nsContentUtils::IsCallerChrome()) { 1.781 + MOZ_CRASH(); 1.782 + } 1.783 + 1.784 + return Open(aPrincipal, aName, Optional<uint64_t>(), aOptions.mStorage, true, 1.785 + aRv); 1.786 +} 1.787 + 1.788 +already_AddRefed<IDBOpenDBRequest> 1.789 +IDBFactory::Open(nsIPrincipal* aPrincipal, const nsAString& aName, 1.790 + const Optional<uint64_t>& aVersion, 1.791 + const Optional<mozilla::dom::StorageType>& aStorageType, 1.792 + bool aDelete, ErrorResult& aRv) 1.793 +{ 1.794 + nsresult rv; 1.795 + 1.796 + nsCString group; 1.797 + nsCString origin; 1.798 + StoragePrivilege privilege; 1.799 + PersistenceType defaultPersistenceType; 1.800 + if (aPrincipal) { 1.801 + rv = QuotaManager::GetInfoFromPrincipal(aPrincipal, &group, &origin, 1.802 + &privilege, 1.803 + &defaultPersistenceType); 1.804 + if (NS_FAILED(rv)) { 1.805 + IDB_REPORT_INTERNAL_ERR(); 1.806 + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.807 + return nullptr; 1.808 + } 1.809 + } 1.810 + else { 1.811 + group = mGroup; 1.812 + origin = mASCIIOrigin; 1.813 + privilege = mPrivilege; 1.814 + defaultPersistenceType = mDefaultPersistenceType; 1.815 + } 1.816 + 1.817 + uint64_t version = 0; 1.818 + if (!aDelete && aVersion.WasPassed()) { 1.819 + if (aVersion.Value() < 1) { 1.820 + aRv.ThrowTypeError(MSG_INVALID_VERSION); 1.821 + return nullptr; 1.822 + } 1.823 + version = aVersion.Value(); 1.824 + } 1.825 + 1.826 + PersistenceType persistenceType = 1.827 + PersistenceTypeFromStorage(aStorageType, defaultPersistenceType); 1.828 + 1.829 + nsRefPtr<IDBOpenDBRequest> request; 1.830 + rv = OpenInternal(aName, version, persistenceType, group, origin, privilege, 1.831 + aDelete, getter_AddRefs(request)); 1.832 + if (NS_FAILED(rv)) { 1.833 + aRv.Throw(rv); 1.834 + return nullptr; 1.835 + } 1.836 + 1.837 + return request.forget(); 1.838 +}