dom/indexedDB/IDBFactory.cpp

changeset 0
6474c204b198
     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 +}

mercurial