dom/indexedDB/OpenDatabaseHelper.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 #include "mozilla/DebugOnly.h"
     7 #include "OpenDatabaseHelper.h"
     9 #include "nsIBFCacheEntry.h"
    10 #include "nsIFile.h"
    12 #include <algorithm>
    13 #include "mozilla/dom/quota/AcquireListener.h"
    14 #include "mozilla/dom/quota/OriginOrPatternString.h"
    15 #include "mozilla/dom/quota/QuotaManager.h"
    16 #include "mozilla/storage.h"
    17 #include "nsEscape.h"
    18 #include "nsNetUtil.h"
    19 #include "nsThreadUtils.h"
    20 #include "snappy/snappy.h"
    22 #include "Client.h"
    23 #include "IDBEvents.h"
    24 #include "IDBFactory.h"
    25 #include "IndexedDatabaseManager.h"
    26 #include "ProfilerHelpers.h"
    27 #include "ReportInternalError.h"
    29 using namespace mozilla;
    30 using namespace mozilla::dom;
    31 USING_INDEXEDDB_NAMESPACE
    32 USING_QUOTA_NAMESPACE
    34 namespace {
    36 // If JS_STRUCTURED_CLONE_VERSION changes then we need to update our major
    37 // schema version.
    38 static_assert(JS_STRUCTURED_CLONE_VERSION == 2,
    39               "Need to update the major schema version.");
    41 // Major schema version. Bump for almost everything.
    42 const uint32_t kMajorSchemaVersion = 14;
    44 // Minor schema version. Should almost always be 0 (maybe bump on release
    45 // branches if we have to).
    46 const uint32_t kMinorSchemaVersion = 0;
    48 // The schema version we store in the SQLite database is a (signed) 32-bit
    49 // integer. The major version is left-shifted 4 bits so the max value is
    50 // 0xFFFFFFF. The minor version occupies the lower 4 bits and its max is 0xF.
    51 static_assert(kMajorSchemaVersion <= 0xFFFFFFF,
    52               "Major version needs to fit in 28 bits.");
    53 static_assert(kMinorSchemaVersion <= 0xF,
    54               "Minor version needs to fit in 4 bits.");
    56 inline
    57 int32_t
    58 MakeSchemaVersion(uint32_t aMajorSchemaVersion,
    59                   uint32_t aMinorSchemaVersion)
    60 {
    61   return int32_t((aMajorSchemaVersion << 4) + aMinorSchemaVersion);
    62 }
    64 const int32_t kSQLiteSchemaVersion = int32_t((kMajorSchemaVersion << 4) +
    65                                              kMinorSchemaVersion);
    67 const uint32_t kGoldenRatioU32 = 0x9E3779B9U;
    69 inline
    70 uint32_t
    71 RotateBitsLeft32(uint32_t value, uint8_t bits)
    72 {
    73   MOZ_ASSERT(bits < 32);
    74   return (value << bits) | (value >> (32 - bits));
    75 }
    77 inline
    78 uint32_t
    79 HashName(const nsAString& aName)
    80 {
    81   const char16_t* str = aName.BeginReading();
    82   size_t length = aName.Length();
    84   uint32_t hash = 0;
    85   for (size_t i = 0; i < length; i++) {
    86     hash = kGoldenRatioU32 * (RotateBitsLeft32(hash, 5) ^ str[i]);
    87   }
    89   return hash;
    90 }
    92 nsresult
    93 GetDatabaseFilename(const nsAString& aName,
    94                     nsAString& aDatabaseFilename)
    95 {
    96   aDatabaseFilename.AppendInt(HashName(aName));
    98   nsCString escapedName;
    99   if (!NS_Escape(NS_ConvertUTF16toUTF8(aName), escapedName, url_XPAlphas)) {
   100     NS_WARNING("Can't escape database name!");
   101     return NS_ERROR_UNEXPECTED;
   102   }
   104   const char* forwardIter = escapedName.BeginReading();
   105   const char* backwardIter = escapedName.EndReading() - 1;
   107   nsCString substring;
   108   while (forwardIter <= backwardIter && substring.Length() < 21) {
   109     if (substring.Length() % 2) {
   110       substring.Append(*backwardIter--);
   111     }
   112     else {
   113       substring.Append(*forwardIter++);
   114     }
   115   }
   117   aDatabaseFilename.Append(NS_ConvertASCIItoUTF16(substring));
   119   return NS_OK;
   120 }
   122 nsresult
   123 CreateFileTables(mozIStorageConnection* aDBConn)
   124 {
   125   AssertIsOnIOThread();
   126   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
   128   PROFILER_LABEL("IndexedDB", "CreateFileTables");
   130   // Table `file`
   131   nsresult rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   132     "CREATE TABLE file ("
   133       "id INTEGER PRIMARY KEY, "
   134       "refcount INTEGER NOT NULL"
   135     ");"
   136   ));
   137   NS_ENSURE_SUCCESS(rv, rv);
   139   rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   140     "CREATE TRIGGER object_data_insert_trigger "
   141     "AFTER INSERT ON object_data "
   142     "FOR EACH ROW "
   143     "WHEN NEW.file_ids IS NOT NULL "
   144     "BEGIN "
   145       "SELECT update_refcount(NULL, NEW.file_ids); "
   146     "END;"
   147   ));
   148   NS_ENSURE_SUCCESS(rv, rv);
   150   rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   151     "CREATE TRIGGER object_data_update_trigger "
   152     "AFTER UPDATE OF file_ids ON object_data "
   153     "FOR EACH ROW "
   154     "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
   155     "BEGIN "
   156       "SELECT update_refcount(OLD.file_ids, NEW.file_ids); "
   157     "END;"
   158   ));
   159   NS_ENSURE_SUCCESS(rv, rv);
   161   rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   162     "CREATE TRIGGER object_data_delete_trigger "
   163     "AFTER DELETE ON object_data "
   164     "FOR EACH ROW WHEN OLD.file_ids IS NOT NULL "
   165     "BEGIN "
   166       "SELECT update_refcount(OLD.file_ids, NULL); "
   167     "END;"
   168   ));
   169   NS_ENSURE_SUCCESS(rv, rv);
   171   rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   172     "CREATE TRIGGER file_update_trigger "
   173     "AFTER UPDATE ON file "
   174     "FOR EACH ROW WHEN NEW.refcount = 0 "
   175     "BEGIN "
   176       "DELETE FROM file WHERE id = OLD.id; "
   177     "END;"
   178   ));
   179   NS_ENSURE_SUCCESS(rv, rv);
   181   return NS_OK;
   182 }
   184 nsresult
   185 CreateTables(mozIStorageConnection* aDBConn)
   186 {
   187   AssertIsOnIOThread();
   188   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
   189   NS_ASSERTION(aDBConn, "Passing a null database connection!");
   191   PROFILER_LABEL("IndexedDB", "CreateTables");
   193   // Table `database`
   194   nsresult rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   195     "CREATE TABLE database ("
   196       "name TEXT NOT NULL, "
   197       "version INTEGER NOT NULL DEFAULT 0"
   198     ");"
   199   ));
   200   NS_ENSURE_SUCCESS(rv, rv);
   202   // Table `object_store`
   203   rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   204     "CREATE TABLE object_store ("
   205       "id INTEGER PRIMARY KEY, "
   206       "auto_increment INTEGER NOT NULL DEFAULT 0, "
   207       "name TEXT NOT NULL, "
   208       "key_path TEXT, "
   209       "UNIQUE (name)"
   210     ");"
   211   ));
   212   NS_ENSURE_SUCCESS(rv, rv);
   214   // Table `object_data`
   215   rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   216     "CREATE TABLE object_data ("
   217       "id INTEGER PRIMARY KEY, "
   218       "object_store_id INTEGER NOT NULL, "
   219       "key_value BLOB DEFAULT NULL, "
   220       "file_ids TEXT, "
   221       "data BLOB NOT NULL, "
   222       "UNIQUE (object_store_id, key_value), "
   223       "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
   224         "CASCADE"
   225     ");"
   226   ));
   227   NS_ENSURE_SUCCESS(rv, rv);
   229   // Table `index`
   230   rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   231     "CREATE TABLE object_store_index ("
   232       "id INTEGER PRIMARY KEY, "
   233       "object_store_id INTEGER NOT NULL, "
   234       "name TEXT NOT NULL, "
   235       "key_path TEXT NOT NULL, "
   236       "unique_index INTEGER NOT NULL, "
   237       "multientry INTEGER NOT NULL, "
   238       "UNIQUE (object_store_id, name), "
   239       "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
   240         "CASCADE"
   241     ");"
   242   ));
   243   NS_ENSURE_SUCCESS(rv, rv);
   245   // Table `index_data`
   246   rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   247     "CREATE TABLE index_data ("
   248       "index_id INTEGER NOT NULL, "
   249       "value BLOB NOT NULL, "
   250       "object_data_key BLOB NOT NULL, "
   251       "object_data_id INTEGER NOT NULL, "
   252       "PRIMARY KEY (index_id, value, object_data_key), "
   253       "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
   254         "CASCADE, "
   255       "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
   256         "CASCADE"
   257     ");"
   258   ));
   259   NS_ENSURE_SUCCESS(rv, rv);
   261   // Need this to make cascading deletes from object_data and object_store fast.
   262   rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   263     "CREATE INDEX index_data_object_data_id_index "
   264     "ON index_data (object_data_id);"
   265   ));
   266   NS_ENSURE_SUCCESS(rv, rv);
   268   // Table `unique_index_data`
   269   rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   270     "CREATE TABLE unique_index_data ("
   271       "index_id INTEGER NOT NULL, "
   272       "value BLOB NOT NULL, "
   273       "object_data_key BLOB NOT NULL, "
   274       "object_data_id INTEGER NOT NULL, "
   275       "PRIMARY KEY (index_id, value, object_data_key), "
   276       "UNIQUE (index_id, value), "
   277       "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
   278         "CASCADE "
   279       "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
   280         "CASCADE"
   281     ");"
   282   ));
   283   NS_ENSURE_SUCCESS(rv, rv);
   285   // Need this to make cascading deletes from object_data and object_store fast.
   286   rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   287     "CREATE INDEX unique_index_data_object_data_id_index "
   288     "ON unique_index_data (object_data_id);"
   289   ));
   290   NS_ENSURE_SUCCESS(rv, rv);
   292   rv = CreateFileTables(aDBConn);
   293   NS_ENSURE_SUCCESS(rv, rv);
   295   rv = aDBConn->SetSchemaVersion(kSQLiteSchemaVersion);
   296   NS_ENSURE_SUCCESS(rv, rv);
   298   return NS_OK;
   299 }
   301 nsresult
   302 UpgradeSchemaFrom4To5(mozIStorageConnection* aConnection)
   303 {
   304   AssertIsOnIOThread();
   305   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
   307   PROFILER_LABEL("IndexedDB", "UpgradeSchemaFrom4To5");
   309   nsresult rv;
   311   // All we changed is the type of the version column, so lets try to
   312   // convert that to an integer, and if we fail, set it to 0.
   313   nsCOMPtr<mozIStorageStatement> stmt;
   314   rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
   315     "SELECT name, version, dataVersion "
   316     "FROM database"
   317   ), getter_AddRefs(stmt));
   318   NS_ENSURE_SUCCESS(rv, rv);
   320   nsString name;
   321   int32_t intVersion;
   322   int64_t dataVersion;
   324   {
   325     mozStorageStatementScoper scoper(stmt);
   327     bool hasResults;
   328     rv = stmt->ExecuteStep(&hasResults);
   329     NS_ENSURE_SUCCESS(rv, rv);
   330     NS_ENSURE_TRUE(hasResults, NS_ERROR_FAILURE);
   332     nsString version;
   333     rv = stmt->GetString(1, version);
   334     NS_ENSURE_SUCCESS(rv, rv);
   336     intVersion = version.ToInteger(&rv);
   337     if (NS_FAILED(rv)) {
   338       intVersion = 0;
   339     }
   341     rv = stmt->GetString(0, name);
   342     NS_ENSURE_SUCCESS(rv, rv);
   344     rv = stmt->GetInt64(2, &dataVersion);
   345     NS_ENSURE_SUCCESS(rv, rv);
   346   }
   348   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   349     "DROP TABLE database"
   350   ));
   351   NS_ENSURE_SUCCESS(rv, rv);
   353   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   354     "CREATE TABLE database ("
   355       "name TEXT NOT NULL, "
   356       "version INTEGER NOT NULL DEFAULT 0, "
   357       "dataVersion INTEGER NOT NULL"
   358     ");"
   359   ));
   360   NS_ENSURE_SUCCESS(rv, rv);
   362   rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
   363     "INSERT INTO database (name, version, dataVersion) "
   364     "VALUES (:name, :version, :dataVersion)"
   365   ), getter_AddRefs(stmt));
   366   NS_ENSURE_SUCCESS(rv, rv);
   368   {
   369     mozStorageStatementScoper scoper(stmt);
   371     rv = stmt->BindStringParameter(0, name);
   372     NS_ENSURE_SUCCESS(rv, rv);
   374     rv = stmt->BindInt32Parameter(1, intVersion);
   375     NS_ENSURE_SUCCESS(rv, rv);
   377     rv = stmt->BindInt64Parameter(2, dataVersion);
   378     NS_ENSURE_SUCCESS(rv, rv);
   380     rv = stmt->Execute();
   381     NS_ENSURE_SUCCESS(rv, rv);
   382   }
   384   rv = aConnection->SetSchemaVersion(5);
   385   NS_ENSURE_SUCCESS(rv, rv);
   387   return NS_OK;
   388 }
   390 nsresult
   391 UpgradeSchemaFrom5To6(mozIStorageConnection* aConnection)
   392 {
   393   AssertIsOnIOThread();
   394   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
   396   PROFILER_LABEL("IndexedDB", "UpgradeSchemaFrom5To6");
   398   // First, drop all the indexes we're no longer going to use.
   399   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   400     "DROP INDEX key_index;"
   401   ));
   402   NS_ENSURE_SUCCESS(rv, rv);
   404   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   405     "DROP INDEX ai_key_index;"
   406   ));
   407   NS_ENSURE_SUCCESS(rv, rv);
   409   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   410     "DROP INDEX value_index;"
   411   ));
   412   NS_ENSURE_SUCCESS(rv, rv);
   414   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   415     "DROP INDEX ai_value_index;"
   416   ));
   417   NS_ENSURE_SUCCESS(rv, rv);
   419   // Now, reorder the columns of object_data to put the blob data last. We do
   420   // this by copying into a temporary table, dropping the original, then copying
   421   // back into a newly created table.
   422   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   423     "CREATE TEMPORARY TABLE temp_upgrade ("
   424       "id INTEGER PRIMARY KEY, "
   425       "object_store_id, "
   426       "key_value, "
   427       "data "
   428     ");"
   429   ));
   430   NS_ENSURE_SUCCESS(rv, rv);
   432   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   433     "INSERT INTO temp_upgrade "
   434       "SELECT id, object_store_id, key_value, data "
   435       "FROM object_data;"
   436   ));
   437   NS_ENSURE_SUCCESS(rv, rv);
   439   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   440     "DROP TABLE object_data;"
   441   ));
   442   NS_ENSURE_SUCCESS(rv, rv);
   444   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   445     "CREATE TABLE object_data ("
   446       "id INTEGER PRIMARY KEY, "
   447       "object_store_id INTEGER NOT NULL, "
   448       "key_value DEFAULT NULL, "
   449       "data BLOB NOT NULL, "
   450       "UNIQUE (object_store_id, key_value), "
   451       "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
   452         "CASCADE"
   453     ");"
   454   ));
   455   NS_ENSURE_SUCCESS(rv, rv);
   457   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   458     "INSERT INTO object_data "
   459       "SELECT id, object_store_id, key_value, data "
   460       "FROM temp_upgrade;"
   461   ));
   462   NS_ENSURE_SUCCESS(rv, rv);
   464   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   465     "DROP TABLE temp_upgrade;"
   466   ));
   467   NS_ENSURE_SUCCESS(rv, rv);
   469   // We need to add a unique constraint to our ai_object_data table. Copy all
   470   // the data out of it using a temporary table as before.
   471   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   472     "CREATE TEMPORARY TABLE temp_upgrade ("
   473       "id INTEGER PRIMARY KEY, "
   474       "object_store_id, "
   475       "data "
   476     ");"
   477   ));
   478   NS_ENSURE_SUCCESS(rv, rv);
   480   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   481     "INSERT INTO temp_upgrade "
   482       "SELECT id, object_store_id, data "
   483       "FROM ai_object_data;"
   484   ));
   485   NS_ENSURE_SUCCESS(rv, rv);
   487   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   488     "DROP TABLE ai_object_data;"
   489   ));
   490   NS_ENSURE_SUCCESS(rv, rv);
   492   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   493     "CREATE TABLE ai_object_data ("
   494       "id INTEGER PRIMARY KEY AUTOINCREMENT, "
   495       "object_store_id INTEGER NOT NULL, "
   496       "data BLOB NOT NULL, "
   497       "UNIQUE (object_store_id, id), "
   498       "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
   499         "CASCADE"
   500     ");"
   501   ));
   502   NS_ENSURE_SUCCESS(rv, rv);
   504   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   505     "INSERT INTO ai_object_data "
   506       "SELECT id, object_store_id, data "
   507       "FROM temp_upgrade;"
   508   ));
   509   NS_ENSURE_SUCCESS(rv, rv);
   511   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   512     "DROP TABLE temp_upgrade;"
   513   ));
   514   NS_ENSURE_SUCCESS(rv, rv);
   516   // Fix up the index_data table. We're reordering the columns as well as
   517   // changing the primary key from being a simple id to being a composite.
   518   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   519     "CREATE TEMPORARY TABLE temp_upgrade ("
   520       "index_id, "
   521       "value, "
   522       "object_data_key, "
   523       "object_data_id "
   524     ");"
   525   ));
   526   NS_ENSURE_SUCCESS(rv, rv);
   528   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   529     "INSERT INTO temp_upgrade "
   530       "SELECT index_id, value, object_data_key, object_data_id "
   531       "FROM index_data;"
   532   ));
   533   NS_ENSURE_SUCCESS(rv, rv);
   535   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   536     "DROP TABLE index_data;"
   537   ));
   538   NS_ENSURE_SUCCESS(rv, rv);
   540   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   541     "CREATE TABLE index_data ("
   542       "index_id INTEGER NOT NULL, "
   543       "value NOT NULL, "
   544       "object_data_key NOT NULL, "
   545       "object_data_id INTEGER NOT NULL, "
   546       "PRIMARY KEY (index_id, value, object_data_key), "
   547       "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
   548         "CASCADE, "
   549       "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
   550         "CASCADE"
   551     ");"
   552   ));
   553   NS_ENSURE_SUCCESS(rv, rv);
   555   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   556     "INSERT OR IGNORE INTO index_data "
   557       "SELECT index_id, value, object_data_key, object_data_id "
   558       "FROM temp_upgrade;"
   559   ));
   560   NS_ENSURE_SUCCESS(rv, rv);
   562   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   563     "DROP TABLE temp_upgrade;"
   564   ));
   565   NS_ENSURE_SUCCESS(rv, rv);
   567   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   568     "CREATE INDEX index_data_object_data_id_index "
   569     "ON index_data (object_data_id);"
   570   ));
   571   NS_ENSURE_SUCCESS(rv, rv);
   573   // Fix up the unique_index_data table. We're reordering the columns as well as
   574   // changing the primary key from being a simple id to being a composite.
   575   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   576     "CREATE TEMPORARY TABLE temp_upgrade ("
   577       "index_id, "
   578       "value, "
   579       "object_data_key, "
   580       "object_data_id "
   581     ");"
   582   ));
   583   NS_ENSURE_SUCCESS(rv, rv);
   585   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   586     "INSERT INTO temp_upgrade "
   587       "SELECT index_id, value, object_data_key, object_data_id "
   588       "FROM unique_index_data;"
   589   ));
   590   NS_ENSURE_SUCCESS(rv, rv);
   592   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   593     "DROP TABLE unique_index_data;"
   594   ));
   595   NS_ENSURE_SUCCESS(rv, rv);
   597   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   598     "CREATE TABLE unique_index_data ("
   599       "index_id INTEGER NOT NULL, "
   600       "value NOT NULL, "
   601       "object_data_key NOT NULL, "
   602       "object_data_id INTEGER NOT NULL, "
   603       "PRIMARY KEY (index_id, value, object_data_key), "
   604       "UNIQUE (index_id, value), "
   605       "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
   606         "CASCADE "
   607       "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
   608         "CASCADE"
   609     ");"
   610   ));
   611   NS_ENSURE_SUCCESS(rv, rv);
   613   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   614     "INSERT INTO unique_index_data "
   615       "SELECT index_id, value, object_data_key, object_data_id "
   616       "FROM temp_upgrade;"
   617   ));
   618   NS_ENSURE_SUCCESS(rv, rv);
   620   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   621     "DROP TABLE temp_upgrade;"
   622   ));
   623   NS_ENSURE_SUCCESS(rv, rv);
   625   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   626     "CREATE INDEX unique_index_data_object_data_id_index "
   627     "ON unique_index_data (object_data_id);"
   628   ));
   629   NS_ENSURE_SUCCESS(rv, rv);
   631   // Fix up the ai_index_data table. We're reordering the columns as well as
   632   // changing the primary key from being a simple id to being a composite.
   633   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   634     "CREATE TEMPORARY TABLE temp_upgrade ("
   635       "index_id, "
   636       "value, "
   637       "ai_object_data_id "
   638     ");"
   639   ));
   640   NS_ENSURE_SUCCESS(rv, rv);
   642   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   643     "INSERT INTO temp_upgrade "
   644       "SELECT index_id, value, ai_object_data_id "
   645       "FROM ai_index_data;"
   646   ));
   647   NS_ENSURE_SUCCESS(rv, rv);
   649   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   650     "DROP TABLE ai_index_data;"
   651   ));
   652   NS_ENSURE_SUCCESS(rv, rv);
   654   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   655     "CREATE TABLE ai_index_data ("
   656       "index_id INTEGER NOT NULL, "
   657       "value NOT NULL, "
   658       "ai_object_data_id INTEGER NOT NULL, "
   659       "PRIMARY KEY (index_id, value, ai_object_data_id), "
   660       "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
   661         "CASCADE, "
   662       "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE "
   663         "CASCADE"
   664     ");"
   665   ));
   666   NS_ENSURE_SUCCESS(rv, rv);
   668   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   669     "INSERT OR IGNORE INTO ai_index_data "
   670       "SELECT index_id, value, ai_object_data_id "
   671       "FROM temp_upgrade;"
   672   ));
   673   NS_ENSURE_SUCCESS(rv, rv);
   675   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   676     "DROP TABLE temp_upgrade;"
   677   ));
   678   NS_ENSURE_SUCCESS(rv, rv);
   680   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   681     "CREATE INDEX ai_index_data_ai_object_data_id_index "
   682     "ON ai_index_data (ai_object_data_id);"
   683   ));
   684   NS_ENSURE_SUCCESS(rv, rv);
   686   // Fix up the ai_unique_index_data table. We're reordering the columns as well
   687   // as changing the primary key from being a simple id to being a composite.
   688   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   689     "CREATE TEMPORARY TABLE temp_upgrade ("
   690       "index_id, "
   691       "value, "
   692       "ai_object_data_id "
   693     ");"
   694   ));
   695   NS_ENSURE_SUCCESS(rv, rv);
   697   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   698     "INSERT INTO temp_upgrade "
   699       "SELECT index_id, value, ai_object_data_id "
   700       "FROM ai_unique_index_data;"
   701   ));
   702   NS_ENSURE_SUCCESS(rv, rv);
   704   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   705     "DROP TABLE ai_unique_index_data;"
   706   ));
   707   NS_ENSURE_SUCCESS(rv, rv);
   709   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   710     "CREATE TABLE ai_unique_index_data ("
   711       "index_id INTEGER NOT NULL, "
   712       "value NOT NULL, "
   713       "ai_object_data_id INTEGER NOT NULL, "
   714       "UNIQUE (index_id, value), "
   715       "PRIMARY KEY (index_id, value, ai_object_data_id), "
   716       "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
   717         "CASCADE, "
   718       "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE "
   719         "CASCADE"
   720     ");"
   721   ));
   722   NS_ENSURE_SUCCESS(rv, rv);
   724   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   725     "INSERT INTO ai_unique_index_data "
   726       "SELECT index_id, value, ai_object_data_id "
   727       "FROM temp_upgrade;"
   728   ));
   729   NS_ENSURE_SUCCESS(rv, rv);
   731   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   732     "DROP TABLE temp_upgrade;"
   733   ));
   734   NS_ENSURE_SUCCESS(rv, rv);
   736   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   737     "CREATE INDEX ai_unique_index_data_ai_object_data_id_index "
   738     "ON ai_unique_index_data (ai_object_data_id);"
   739   ));
   740   NS_ENSURE_SUCCESS(rv, rv);
   742   rv = aConnection->SetSchemaVersion(6);
   743   NS_ENSURE_SUCCESS(rv, rv);
   745   return NS_OK;
   746 }
   748 nsresult
   749 UpgradeSchemaFrom6To7(mozIStorageConnection* aConnection)
   750 {
   751   AssertIsOnIOThread();
   752   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
   754   PROFILER_LABEL("IndexedDB", "UpgradeSchemaFrom6To7");
   756   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   757     "CREATE TEMPORARY TABLE temp_upgrade ("
   758       "id, "
   759       "name, "
   760       "key_path, "
   761       "auto_increment"
   762     ");"
   763   ));
   764   NS_ENSURE_SUCCESS(rv, rv);
   766   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   767     "INSERT INTO temp_upgrade "
   768       "SELECT id, name, key_path, auto_increment "
   769       "FROM object_store;"
   770   ));
   771   NS_ENSURE_SUCCESS(rv, rv);
   773   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   774     "DROP TABLE object_store;"
   775   ));
   776   NS_ENSURE_SUCCESS(rv, rv);
   778   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   779     "CREATE TABLE object_store ("
   780       "id INTEGER PRIMARY KEY, "
   781       "auto_increment INTEGER NOT NULL DEFAULT 0, "
   782       "name TEXT NOT NULL, "
   783       "key_path TEXT, "
   784       "UNIQUE (name)"
   785     ");"
   786   ));
   787   NS_ENSURE_SUCCESS(rv, rv);
   789   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   790     "INSERT INTO object_store "
   791       "SELECT id, auto_increment, name, nullif(key_path, '') "
   792       "FROM temp_upgrade;"
   793   ));
   794   NS_ENSURE_SUCCESS(rv, rv);
   796   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   797     "DROP TABLE temp_upgrade;"
   798   ));
   799   NS_ENSURE_SUCCESS(rv, rv);
   801   rv = aConnection->SetSchemaVersion(7);
   802   NS_ENSURE_SUCCESS(rv, rv);
   804   return NS_OK;
   805 }
   807 nsresult
   808 UpgradeSchemaFrom7To8(mozIStorageConnection* aConnection)
   809 {
   810   AssertIsOnIOThread();
   811   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
   813   PROFILER_LABEL("IndexedDB", "UpgradeSchemaFrom7To8");
   815   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   816     "CREATE TEMPORARY TABLE temp_upgrade ("
   817       "id, "
   818       "object_store_id, "
   819       "name, "
   820       "key_path, "
   821       "unique_index, "
   822       "object_store_autoincrement"
   823     ");"
   824   ));
   825   NS_ENSURE_SUCCESS(rv, rv);
   827   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   828     "INSERT INTO temp_upgrade "
   829       "SELECT id, object_store_id, name, key_path, "
   830       "unique_index, object_store_autoincrement "
   831       "FROM object_store_index;"
   832   ));
   833   NS_ENSURE_SUCCESS(rv, rv);
   835   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   836     "DROP TABLE object_store_index;"
   837   ));
   838   NS_ENSURE_SUCCESS(rv, rv);
   840   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   841     "CREATE TABLE object_store_index ("
   842       "id INTEGER, "
   843       "object_store_id INTEGER NOT NULL, "
   844       "name TEXT NOT NULL, "
   845       "key_path TEXT NOT NULL, "
   846       "unique_index INTEGER NOT NULL, "
   847       "multientry INTEGER NOT NULL, "
   848       "object_store_autoincrement INTERGER NOT NULL, "
   849       "PRIMARY KEY (id), "
   850       "UNIQUE (object_store_id, name), "
   851       "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
   852         "CASCADE"
   853     ");"
   854   ));
   855   NS_ENSURE_SUCCESS(rv, rv);
   857   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   858     "INSERT INTO object_store_index "
   859       "SELECT id, object_store_id, name, key_path, "
   860       "unique_index, 0, object_store_autoincrement "
   861       "FROM temp_upgrade;"
   862   ));
   863   NS_ENSURE_SUCCESS(rv, rv);
   865   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   866     "DROP TABLE temp_upgrade;"
   867   ));
   868   NS_ENSURE_SUCCESS(rv, rv);
   870   rv = aConnection->SetSchemaVersion(8);
   871   NS_ENSURE_SUCCESS(rv, rv);
   873   return NS_OK;
   874 }
   876 class CompressDataBlobsFunction MOZ_FINAL : public mozIStorageFunction
   877 {
   878 public:
   879   NS_DECL_ISUPPORTS
   881   NS_IMETHOD
   882   OnFunctionCall(mozIStorageValueArray* aArguments,
   883                  nsIVariant** aResult)
   884   {
   885     PROFILER_LABEL("IndexedDB", "CompressDataBlobsFunction::OnFunctionCall");
   887     uint32_t argc;
   888     nsresult rv = aArguments->GetNumEntries(&argc);
   889     NS_ENSURE_SUCCESS(rv, rv);
   891     if (argc != 1) {
   892       NS_WARNING("Don't call me with the wrong number of arguments!");
   893       return NS_ERROR_UNEXPECTED;
   894     }
   896     int32_t type;
   897     rv = aArguments->GetTypeOfIndex(0, &type);
   898     NS_ENSURE_SUCCESS(rv, rv);
   900     if (type != mozIStorageStatement::VALUE_TYPE_BLOB) {
   901       NS_WARNING("Don't call me with the wrong type of arguments!");
   902       return NS_ERROR_UNEXPECTED;
   903     }
   905     const uint8_t* uncompressed;
   906     uint32_t uncompressedLength;
   907     rv = aArguments->GetSharedBlob(0, &uncompressedLength, &uncompressed);
   908     NS_ENSURE_SUCCESS(rv, rv);
   910     size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength);
   911     char* compressed = (char*)moz_malloc(compressedLength);
   912     NS_ENSURE_TRUE(compressed, NS_ERROR_OUT_OF_MEMORY);
   914     snappy::RawCompress(reinterpret_cast<const char*>(uncompressed),
   915                         uncompressedLength, compressed, &compressedLength);
   917     std::pair<uint8_t *, int> data((uint8_t*)compressed,
   918                                    int(compressedLength));
   919     // The variant takes ownership of | compressed |.
   920     nsCOMPtr<nsIVariant> result = new mozilla::storage::AdoptedBlobVariant(data);
   922     result.forget(aResult);
   923     return NS_OK;
   924   }
   925 };
   927 NS_IMPL_ISUPPORTS(CompressDataBlobsFunction, mozIStorageFunction)
   929 nsresult
   930 UpgradeSchemaFrom8To9_0(mozIStorageConnection* aConnection)
   931 {
   932   AssertIsOnIOThread();
   933   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
   935   PROFILER_LABEL("IndexedDB", "UpgradeSchemaFrom8To9_0");
   937   // We no longer use the dataVersion column.
   938   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   939     "UPDATE database SET dataVersion = 0;"
   940   ));
   941   NS_ENSURE_SUCCESS(rv, rv);
   943   nsCOMPtr<mozIStorageFunction> compressor = new CompressDataBlobsFunction();
   945   NS_NAMED_LITERAL_CSTRING(compressorName, "compress");
   947   rv = aConnection->CreateFunction(compressorName, 1, compressor);
   948   NS_ENSURE_SUCCESS(rv, rv);
   950   // Turn off foreign key constraints before we do anything here.
   951   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   952     "UPDATE object_data SET data = compress(data);"
   953   ));
   954   NS_ENSURE_SUCCESS(rv, rv);
   956   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   957     "UPDATE ai_object_data SET data = compress(data);"
   958   ));
   959   NS_ENSURE_SUCCESS(rv, rv);
   961   rv = aConnection->RemoveFunction(compressorName);
   962   NS_ENSURE_SUCCESS(rv, rv);
   964   rv = aConnection->SetSchemaVersion(MakeSchemaVersion(9, 0));
   965   NS_ENSURE_SUCCESS(rv, rv);
   967   return NS_OK;
   968 }
   970 nsresult
   971 UpgradeSchemaFrom9_0To10_0(mozIStorageConnection* aConnection)
   972 {
   973   AssertIsOnIOThread();
   974   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
   976   PROFILER_LABEL("IndexedDB", "UpgradeSchemaFrom9_0To10_0");
   978   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   979     "ALTER TABLE object_data ADD COLUMN file_ids TEXT;"
   980   ));
   981   NS_ENSURE_SUCCESS(rv, rv);
   983   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   984     "ALTER TABLE ai_object_data ADD COLUMN file_ids TEXT;"
   985   ));
   986   NS_ENSURE_SUCCESS(rv, rv);
   988   rv = CreateFileTables(aConnection);
   989   NS_ENSURE_SUCCESS(rv, rv);
   991   rv = aConnection->SetSchemaVersion(MakeSchemaVersion(10, 0));
   992   NS_ENSURE_SUCCESS(rv, rv);
   994   return NS_OK;
   995 }
   997 nsresult
   998 UpgradeSchemaFrom10_0To11_0(mozIStorageConnection* aConnection)
   999 {
  1000   AssertIsOnIOThread();
  1001   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
  1003   PROFILER_LABEL("IndexedDB", "UpgradeSchemaFrom10_0To11_0");
  1005   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1006     "CREATE TEMPORARY TABLE temp_upgrade ("
  1007       "id, "
  1008       "object_store_id, "
  1009       "name, "
  1010       "key_path, "
  1011       "unique_index, "
  1012       "multientry"
  1013     ");"
  1014   ));
  1015   NS_ENSURE_SUCCESS(rv, rv);
  1017   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1018     "INSERT INTO temp_upgrade "
  1019       "SELECT id, object_store_id, name, key_path, "
  1020       "unique_index, multientry "
  1021       "FROM object_store_index;"
  1022   ));
  1023   NS_ENSURE_SUCCESS(rv, rv);
  1025   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1026     "DROP TABLE object_store_index;"
  1027   ));
  1028   NS_ENSURE_SUCCESS(rv, rv);
  1030   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1031     "CREATE TABLE object_store_index ("
  1032       "id INTEGER PRIMARY KEY, "
  1033       "object_store_id INTEGER NOT NULL, "
  1034       "name TEXT NOT NULL, "
  1035       "key_path TEXT NOT NULL, "
  1036       "unique_index INTEGER NOT NULL, "
  1037       "multientry INTEGER NOT NULL, "
  1038       "UNIQUE (object_store_id, name), "
  1039       "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
  1040         "CASCADE"
  1041     ");"
  1042   ));
  1043   NS_ENSURE_SUCCESS(rv, rv);
  1045   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1046     "INSERT INTO object_store_index "
  1047       "SELECT id, object_store_id, name, key_path, "
  1048       "unique_index, multientry "
  1049       "FROM temp_upgrade;"
  1050   ));
  1051   NS_ENSURE_SUCCESS(rv, rv);
  1053   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1054     "DROP TABLE temp_upgrade;"
  1055   ));
  1056   NS_ENSURE_SUCCESS(rv, rv);
  1058   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1059     "DROP TRIGGER object_data_insert_trigger;"
  1060   ));
  1061   NS_ENSURE_SUCCESS(rv, rv);
  1063   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1064     "INSERT INTO object_data (object_store_id, key_value, data, file_ids) "
  1065       "SELECT object_store_id, id, data, file_ids "
  1066       "FROM ai_object_data;"
  1067   ));
  1068   NS_ENSURE_SUCCESS(rv, rv);
  1070   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1071     "CREATE TRIGGER object_data_insert_trigger "
  1072     "AFTER INSERT ON object_data "
  1073     "FOR EACH ROW "
  1074     "WHEN NEW.file_ids IS NOT NULL "
  1075     "BEGIN "
  1076       "SELECT update_refcount(NULL, NEW.file_ids); "
  1077     "END;"
  1078   ));
  1079   NS_ENSURE_SUCCESS(rv, rv);
  1081   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1082     "INSERT INTO index_data (index_id, value, object_data_key, object_data_id) "
  1083       "SELECT ai_index_data.index_id, ai_index_data.value, ai_index_data.ai_object_data_id, object_data.id "
  1084       "FROM ai_index_data "
  1085       "INNER JOIN object_store_index ON "
  1086         "object_store_index.id = ai_index_data.index_id "
  1087       "INNER JOIN object_data ON "
  1088         "object_data.object_store_id = object_store_index.object_store_id AND "
  1089         "object_data.key_value = ai_index_data.ai_object_data_id;"
  1090   ));
  1091   NS_ENSURE_SUCCESS(rv, rv);
  1093   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1094     "INSERT INTO unique_index_data (index_id, value, object_data_key, object_data_id) "
  1095       "SELECT ai_unique_index_data.index_id, ai_unique_index_data.value, ai_unique_index_data.ai_object_data_id, object_data.id "
  1096       "FROM ai_unique_index_data "
  1097       "INNER JOIN object_store_index ON "
  1098         "object_store_index.id = ai_unique_index_data.index_id "
  1099       "INNER JOIN object_data ON "
  1100         "object_data.object_store_id = object_store_index.object_store_id AND "
  1101         "object_data.key_value = ai_unique_index_data.ai_object_data_id;"
  1102   ));
  1103   NS_ENSURE_SUCCESS(rv, rv);
  1105   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1106     "UPDATE object_store "
  1107       "SET auto_increment = (SELECT max(id) FROM ai_object_data) + 1 "
  1108       "WHERE auto_increment;"
  1109   ));
  1110   NS_ENSURE_SUCCESS(rv, rv);
  1112   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1113     "DROP TABLE ai_unique_index_data;"
  1114   ));
  1115   NS_ENSURE_SUCCESS(rv, rv);
  1117   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1118     "DROP TABLE ai_index_data;"
  1119   ));
  1120   NS_ENSURE_SUCCESS(rv, rv);
  1122   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1123     "DROP TABLE ai_object_data;"
  1124   ));
  1125   NS_ENSURE_SUCCESS(rv, rv);
  1127   rv = aConnection->SetSchemaVersion(MakeSchemaVersion(11, 0));
  1128   NS_ENSURE_SUCCESS(rv, rv);
  1130   return NS_OK;
  1133 class EncodeKeysFunction MOZ_FINAL : public mozIStorageFunction
  1135 public:
  1136   NS_DECL_ISUPPORTS
  1138   NS_IMETHOD
  1139   OnFunctionCall(mozIStorageValueArray* aArguments,
  1140                  nsIVariant** aResult)
  1142     PROFILER_LABEL("IndexedDB", "EncodeKeysFunction::OnFunctionCall");
  1144     uint32_t argc;
  1145     nsresult rv = aArguments->GetNumEntries(&argc);
  1146     NS_ENSURE_SUCCESS(rv, rv);
  1148     if (argc != 1) {
  1149       NS_WARNING("Don't call me with the wrong number of arguments!");
  1150       return NS_ERROR_UNEXPECTED;
  1153     int32_t type;
  1154     rv = aArguments->GetTypeOfIndex(0, &type);
  1155     NS_ENSURE_SUCCESS(rv, rv);
  1157     Key key;
  1158     if (type == mozIStorageStatement::VALUE_TYPE_INTEGER) {
  1159       int64_t intKey;
  1160       aArguments->GetInt64(0, &intKey);
  1161       key.SetFromInteger(intKey);
  1163     else if (type == mozIStorageStatement::VALUE_TYPE_TEXT) {
  1164       nsString stringKey;
  1165       aArguments->GetString(0, stringKey);
  1166       key.SetFromString(stringKey);
  1168     else {
  1169       NS_WARNING("Don't call me with the wrong type of arguments!");
  1170       return NS_ERROR_UNEXPECTED;
  1173     const nsCString& buffer = key.GetBuffer();
  1175     std::pair<const void *, int> data(static_cast<const void*>(buffer.get()),
  1176                                       int(buffer.Length()));
  1178     nsCOMPtr<nsIVariant> result = new mozilla::storage::BlobVariant(data);
  1180     result.forget(aResult);
  1181     return NS_OK;
  1183 };
  1185 NS_IMPL_ISUPPORTS(EncodeKeysFunction, mozIStorageFunction)
  1187 nsresult
  1188 UpgradeSchemaFrom11_0To12_0(mozIStorageConnection* aConnection)
  1190   AssertIsOnIOThread();
  1191   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
  1193   PROFILER_LABEL("IndexedDB", "UpgradeSchemaFrom11_0To12_0");
  1195   NS_NAMED_LITERAL_CSTRING(encoderName, "encode");
  1197   nsCOMPtr<mozIStorageFunction> encoder = new EncodeKeysFunction();
  1199   nsresult rv = aConnection->CreateFunction(encoderName, 1, encoder);
  1200   NS_ENSURE_SUCCESS(rv, rv);
  1202   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1203     "CREATE TEMPORARY TABLE temp_upgrade ("
  1204       "id INTEGER PRIMARY KEY, "
  1205       "object_store_id, "
  1206       "key_value, "
  1207       "data, "
  1208       "file_ids "
  1209     ");"
  1210   ));
  1211   NS_ENSURE_SUCCESS(rv, rv);
  1213   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1214     "INSERT INTO temp_upgrade "
  1215       "SELECT id, object_store_id, encode(key_value), data, file_ids "
  1216       "FROM object_data;"
  1217   ));
  1218   NS_ENSURE_SUCCESS(rv, rv);
  1220   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1221     "DROP TABLE object_data;"
  1222   ));
  1223   NS_ENSURE_SUCCESS(rv, rv);
  1225   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1226     "CREATE TABLE object_data ("
  1227       "id INTEGER PRIMARY KEY, "
  1228       "object_store_id INTEGER NOT NULL, "
  1229       "key_value BLOB DEFAULT NULL, "
  1230       "file_ids TEXT, "
  1231       "data BLOB NOT NULL, "
  1232       "UNIQUE (object_store_id, key_value), "
  1233       "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
  1234         "CASCADE"
  1235     ");"
  1236   ));
  1237   NS_ENSURE_SUCCESS(rv, rv);
  1239   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1240     "INSERT INTO object_data "
  1241       "SELECT id, object_store_id, key_value, file_ids, data "
  1242       "FROM temp_upgrade;"
  1243   ));
  1244   NS_ENSURE_SUCCESS(rv, rv);
  1246   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1247     "DROP TABLE temp_upgrade;"
  1248   ));
  1249   NS_ENSURE_SUCCESS(rv, rv);
  1251   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1252     "CREATE TRIGGER object_data_insert_trigger "
  1253     "AFTER INSERT ON object_data "
  1254     "FOR EACH ROW "
  1255     "WHEN NEW.file_ids IS NOT NULL "
  1256     "BEGIN "
  1257       "SELECT update_refcount(NULL, NEW.file_ids); "
  1258     "END;"
  1259   ));
  1260   NS_ENSURE_SUCCESS(rv, rv);
  1262   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1263     "CREATE TRIGGER object_data_update_trigger "
  1264     "AFTER UPDATE OF file_ids ON object_data "
  1265     "FOR EACH ROW "
  1266     "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
  1267     "BEGIN "
  1268       "SELECT update_refcount(OLD.file_ids, NEW.file_ids); "
  1269     "END;"
  1270   ));
  1271   NS_ENSURE_SUCCESS(rv, rv);
  1273   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1274     "CREATE TRIGGER object_data_delete_trigger "
  1275     "AFTER DELETE ON object_data "
  1276     "FOR EACH ROW WHEN OLD.file_ids IS NOT NULL "
  1277     "BEGIN "
  1278       "SELECT update_refcount(OLD.file_ids, NULL); "
  1279     "END;"
  1280   ));
  1281   NS_ENSURE_SUCCESS(rv, rv);
  1283   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1284     "CREATE TEMPORARY TABLE temp_upgrade ("
  1285       "index_id, "
  1286       "value, "
  1287       "object_data_key, "
  1288       "object_data_id "
  1289     ");"
  1290   ));
  1291   NS_ENSURE_SUCCESS(rv, rv);
  1293   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1294     "INSERT INTO temp_upgrade "
  1295       "SELECT index_id, encode(value), encode(object_data_key), object_data_id "
  1296       "FROM index_data;"
  1297   ));
  1298   NS_ENSURE_SUCCESS(rv, rv);
  1300   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1301     "DROP TABLE index_data;"
  1302   ));
  1303   NS_ENSURE_SUCCESS(rv, rv);
  1305   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1306     "CREATE TABLE index_data ("
  1307       "index_id INTEGER NOT NULL, "
  1308       "value BLOB NOT NULL, "
  1309       "object_data_key BLOB NOT NULL, "
  1310       "object_data_id INTEGER NOT NULL, "
  1311       "PRIMARY KEY (index_id, value, object_data_key), "
  1312       "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
  1313         "CASCADE, "
  1314       "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
  1315         "CASCADE"
  1316     ");"
  1317   ));
  1318   NS_ENSURE_SUCCESS(rv, rv);
  1320   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1321     "INSERT INTO index_data "
  1322       "SELECT index_id, value, object_data_key, object_data_id "
  1323       "FROM temp_upgrade;"
  1324   ));
  1325   NS_ENSURE_SUCCESS(rv, rv);
  1327   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1328     "DROP TABLE temp_upgrade;"
  1329   ));
  1330   NS_ENSURE_SUCCESS(rv, rv);
  1332   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1333     "CREATE INDEX index_data_object_data_id_index "
  1334     "ON index_data (object_data_id);"
  1335   ));
  1336   NS_ENSURE_SUCCESS(rv, rv);
  1338   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1339     "CREATE TEMPORARY TABLE temp_upgrade ("
  1340       "index_id, "
  1341       "value, "
  1342       "object_data_key, "
  1343       "object_data_id "
  1344     ");"
  1345   ));
  1346   NS_ENSURE_SUCCESS(rv, rv);
  1348   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1349     "INSERT INTO temp_upgrade "
  1350       "SELECT index_id, encode(value), encode(object_data_key), object_data_id "
  1351       "FROM unique_index_data;"
  1352   ));
  1353   NS_ENSURE_SUCCESS(rv, rv);
  1355   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1356     "DROP TABLE unique_index_data;"
  1357   ));
  1358   NS_ENSURE_SUCCESS(rv, rv);
  1360   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1361     "CREATE TABLE unique_index_data ("
  1362       "index_id INTEGER NOT NULL, "
  1363       "value BLOB NOT NULL, "
  1364       "object_data_key BLOB NOT NULL, "
  1365       "object_data_id INTEGER NOT NULL, "
  1366       "PRIMARY KEY (index_id, value, object_data_key), "
  1367       "UNIQUE (index_id, value), "
  1368       "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
  1369         "CASCADE "
  1370       "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
  1371         "CASCADE"
  1372     ");"
  1373   ));
  1374   NS_ENSURE_SUCCESS(rv, rv);
  1376   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1377     "INSERT INTO unique_index_data "
  1378       "SELECT index_id, value, object_data_key, object_data_id "
  1379       "FROM temp_upgrade;"
  1380   ));
  1381   NS_ENSURE_SUCCESS(rv, rv);
  1383   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1384     "DROP TABLE temp_upgrade;"
  1385   ));
  1386   NS_ENSURE_SUCCESS(rv, rv);
  1388   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1389     "CREATE INDEX unique_index_data_object_data_id_index "
  1390     "ON unique_index_data (object_data_id);"
  1391   ));
  1392   NS_ENSURE_SUCCESS(rv, rv);
  1394   rv = aConnection->RemoveFunction(encoderName);
  1395   NS_ENSURE_SUCCESS(rv, rv);
  1397   rv = aConnection->SetSchemaVersion(MakeSchemaVersion(12, 0));
  1398   NS_ENSURE_SUCCESS(rv, rv);
  1400   return NS_OK;
  1403 nsresult
  1404 UpgradeSchemaFrom12_0To13_0(mozIStorageConnection* aConnection,
  1405                             bool* aVacuumNeeded)
  1407   AssertIsOnIOThread();
  1408   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
  1410   PROFILER_LABEL("IndexedDB", "UpgradeSchemaFrom12_0To13_0");
  1412   nsresult rv;
  1414 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
  1415   int32_t defaultPageSize;
  1416   rv = aConnection->GetDefaultPageSize(&defaultPageSize);
  1417   NS_ENSURE_SUCCESS(rv, rv);
  1419   // Enable auto_vacuum mode and update the page size to the platform default.
  1420   nsAutoCString upgradeQuery("PRAGMA auto_vacuum = FULL; PRAGMA page_size = ");
  1421   upgradeQuery.AppendInt(defaultPageSize);
  1423   rv = aConnection->ExecuteSimpleSQL(upgradeQuery);
  1424   NS_ENSURE_SUCCESS(rv, rv);
  1426   *aVacuumNeeded = true;
  1427 #endif
  1429   rv = aConnection->SetSchemaVersion(MakeSchemaVersion(13, 0));
  1430   NS_ENSURE_SUCCESS(rv, rv);
  1432   return NS_OK;
  1435 nsresult
  1436 UpgradeSchemaFrom13_0To14_0(mozIStorageConnection* aConnection)
  1438   // The only change between 13 and 14 was a different structured
  1439   // clone format, but it's backwards-compatible.
  1440   nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(14, 0));
  1441   NS_ENSURE_SUCCESS(rv, rv);
  1443   return NS_OK;
  1446 class VersionChangeEventsRunnable;
  1448 class SetVersionHelper : public AsyncConnectionHelper,
  1449                          public IDBTransactionListener,
  1450                          public AcquireListener
  1452   friend class VersionChangeEventsRunnable;
  1454 public:
  1455   SetVersionHelper(IDBTransaction* aTransaction,
  1456                    IDBOpenDBRequest* aRequest,
  1457                    OpenDatabaseHelper* aHelper,
  1458                    uint64_t aRequestedVersion,
  1459                    uint64_t aCurrentVersion)
  1460   : AsyncConnectionHelper(aTransaction, aRequest),
  1461     mOpenRequest(aRequest), mOpenHelper(aHelper),
  1462     mRequestedVersion(aRequestedVersion),
  1463     mCurrentVersion(aCurrentVersion)
  1465     mTransaction->SetTransactionListener(this);
  1468   NS_DECL_ISUPPORTS_INHERITED
  1470   virtual nsresult GetSuccessResult(JSContext* aCx,
  1471                                     JS::MutableHandle<JS::Value> aVal) MOZ_OVERRIDE;
  1473   virtual nsresult
  1474   OnExclusiveAccessAcquired() MOZ_OVERRIDE;
  1476 protected:
  1477   virtual nsresult Init() MOZ_OVERRIDE;
  1479   virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection)
  1480                                   MOZ_OVERRIDE;
  1482   // SetVersionHelper never fires an error event at the request.  It hands that
  1483   // responsibility back to the OpenDatabaseHelper
  1484   virtual void OnError() MOZ_OVERRIDE
  1485   { }
  1487   // Need an upgradeneeded event here.
  1488   virtual already_AddRefed<nsIDOMEvent> CreateSuccessEvent(
  1489     mozilla::dom::EventTarget* aOwner) MOZ_OVERRIDE;
  1491   virtual nsresult NotifyTransactionPreComplete(IDBTransaction* aTransaction)
  1492                                                 MOZ_OVERRIDE;
  1493   virtual nsresult NotifyTransactionPostComplete(IDBTransaction* aTransaction)
  1494                                                  MOZ_OVERRIDE;
  1496   virtual ChildProcessSendResult
  1497   SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE
  1499     return Success_NotSent;
  1502   virtual nsresult UnpackResponseFromParentProcess(
  1503                                             const ResponseValue& aResponseValue)
  1504                                             MOZ_OVERRIDE
  1506     MOZ_CRASH("Should never get here!");
  1509   uint64_t RequestedVersion() const
  1511     return mRequestedVersion;
  1514 private:
  1515   // In-params
  1516   nsRefPtr<IDBOpenDBRequest> mOpenRequest;
  1517   nsRefPtr<OpenDatabaseHelper> mOpenHelper;
  1518   uint64_t mRequestedVersion;
  1519   uint64_t mCurrentVersion;
  1520 };
  1522 class DeleteDatabaseHelper : public AsyncConnectionHelper,
  1523                              public AcquireListener
  1525   friend class VersionChangeEventsRunnable;
  1526 public:
  1527   DeleteDatabaseHelper(IDBOpenDBRequest* aRequest,
  1528                        OpenDatabaseHelper* aHelper,
  1529                        uint64_t aCurrentVersion,
  1530                        const nsAString& aName,
  1531                        const nsACString& aGroup,
  1532                        const nsACString& aASCIIOrigin,
  1533                        PersistenceType aPersistenceType)
  1534   : AsyncConnectionHelper(static_cast<IDBDatabase*>(nullptr), aRequest),
  1535     mOpenHelper(aHelper), mOpenRequest(aRequest),
  1536     mCurrentVersion(aCurrentVersion), mName(aName),
  1537     mGroup(aGroup), mASCIIOrigin(aASCIIOrigin),
  1538     mPersistenceType(aPersistenceType)
  1539   { }
  1541   NS_DECL_ISUPPORTS_INHERITED
  1543   nsresult GetSuccessResult(JSContext* aCx,
  1544                             JS::MutableHandle<JS::Value> aVal);
  1546   void ReleaseMainThreadObjects()
  1548     mOpenHelper = nullptr;
  1549     mOpenRequest = nullptr;
  1551     AsyncConnectionHelper::ReleaseMainThreadObjects();
  1554   virtual nsresult
  1555   OnExclusiveAccessAcquired() MOZ_OVERRIDE;
  1557 protected:
  1558   nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
  1559   nsresult Init();
  1561   // DeleteDatabaseHelper never fires events at the request.  It hands that
  1562   // responsibility back to the OpenDatabaseHelper
  1563   void OnError()
  1565     mOpenHelper->NotifyDeleteFinished();
  1568   nsresult OnSuccess()
  1570     return mOpenHelper->NotifyDeleteFinished();
  1573   uint64_t RequestedVersion() const
  1575     return 0;
  1578   virtual ChildProcessSendResult
  1579   SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE
  1581     return Success_NotSent;
  1584   virtual nsresult UnpackResponseFromParentProcess(
  1585                                             const ResponseValue& aResponseValue)
  1586                                             MOZ_OVERRIDE
  1588     MOZ_CRASH("Should never get here!");
  1591 private:
  1592   // In-params
  1593   nsRefPtr<OpenDatabaseHelper> mOpenHelper;
  1594   nsRefPtr<IDBOpenDBRequest> mOpenRequest;
  1595   uint64_t mCurrentVersion;
  1596   nsString mName;
  1597   nsCString mGroup;
  1598   nsCString mASCIIOrigin;
  1599   PersistenceType mPersistenceType;
  1600 };
  1602 // Responsible for firing "versionchange" events at all live and non-closed
  1603 // databases, and for firing a "blocked" event at the requesting database if any
  1604 // databases fail to close.
  1605 class VersionChangeEventsRunnable : public nsRunnable
  1607 public:
  1608   VersionChangeEventsRunnable(
  1609                       IDBDatabase* aRequestingDatabase,
  1610                       IDBOpenDBRequest* aRequest,
  1611                       nsTArray<nsCOMPtr<nsIOfflineStorage> >& aWaitingDatabases,
  1612                       int64_t aOldVersion,
  1613                       int64_t aNewVersion)
  1614   : mRequestingDatabase(aRequestingDatabase),
  1615     mRequest(aRequest),
  1616     mOldVersion(aOldVersion),
  1617     mNewVersion(aNewVersion)
  1619     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1620     NS_ASSERTION(aRequestingDatabase, "Null pointer!");
  1621     NS_ASSERTION(aRequest, "Null pointer!");
  1623     mWaitingDatabases.SwapElements(aWaitingDatabases);
  1626   NS_IMETHOD Run()
  1628     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1630     PROFILER_MAIN_THREAD_LABEL("IndexedDB", "VersionChangeEventsRunnable::Run");
  1632     // Fire version change events at all of the databases that are not already
  1633     // closed. Also kick bfcached documents out of bfcache.
  1634     uint32_t count = mWaitingDatabases.Length();
  1635     for (uint32_t index = 0; index < count; index++) {
  1636       IDBDatabase* database =
  1637         IDBDatabase::FromStorage(mWaitingDatabases[index]);
  1638       NS_ASSERTION(database, "This shouldn't be null!");
  1640       if (database->IsClosed()) {
  1641         continue;
  1644       // First check if the document the IDBDatabase is part of is bfcached.
  1645       nsCOMPtr<nsIDocument> ownerDoc = database->GetOwnerDocument();
  1646       nsIBFCacheEntry* bfCacheEntry;
  1647       if (ownerDoc && (bfCacheEntry = ownerDoc->GetBFCacheEntry())) {
  1648         bfCacheEntry->RemoveFromBFCacheSync();
  1649         NS_ASSERTION(database->IsClosed(),
  1650                      "Kicking doc out of bfcache should have closed database");
  1651         continue;
  1654       // Next check if it's in the process of being bfcached.
  1655       nsPIDOMWindow* owner = database->GetOwner();
  1656       if (owner && owner->IsFrozen()) {
  1657         // We can't kick the document out of the bfcache because it's not yet
  1658         // fully in the bfcache.  Instead we'll abort everything for the window
  1659         // and mark it as not-bfcacheable.
  1660         QuotaManager* quotaManager = QuotaManager::Get();
  1661         NS_ASSERTION(quotaManager, "Huh?");
  1662         quotaManager->AbortCloseStoragesForWindow(owner);
  1664         NS_ASSERTION(database->IsClosed(),
  1665                    "AbortCloseStoragesForWindow should have closed database");
  1666         ownerDoc->DisallowBFCaching();
  1667         continue;
  1670       // Otherwise fire a versionchange event.
  1671       nsRefPtr<Event> event = 
  1672         IDBVersionChangeEvent::Create(database, mOldVersion, mNewVersion);
  1673       NS_ENSURE_TRUE(event, NS_ERROR_FAILURE);
  1675       bool dummy;
  1676       database->DispatchEvent(event, &dummy);
  1679     // Now check to see if any didn't close. If there are some running still
  1680     // then fire the blocked event.
  1681     for (uint32_t index = 0; index < count; index++) {
  1682       if (!mWaitingDatabases[index]->IsClosed()) {
  1683         nsRefPtr<Event> event =
  1684           IDBVersionChangeEvent::CreateBlocked(mRequest,
  1685                                                mOldVersion, mNewVersion);
  1686         NS_ENSURE_TRUE(event, NS_ERROR_FAILURE);
  1688         bool dummy;
  1689         mRequest->DispatchEvent(event, &dummy);
  1691         break;
  1695     return NS_OK;
  1698   template <class T>
  1699   static
  1700   void QueueVersionChange(nsTArray<nsCOMPtr<nsIOfflineStorage> >& aDatabases,
  1701                           void* aClosure);
  1702 private:
  1703   nsRefPtr<IDBDatabase> mRequestingDatabase;
  1704   nsRefPtr<IDBOpenDBRequest> mRequest;
  1705   nsTArray<nsCOMPtr<nsIOfflineStorage> > mWaitingDatabases;
  1706   int64_t mOldVersion;
  1707   int64_t mNewVersion;
  1708 };
  1710 } // anonymous namespace
  1712 NS_IMPL_ISUPPORTS(OpenDatabaseHelper, nsIRunnable)
  1714 nsresult
  1715 OpenDatabaseHelper::Init()
  1717   QuotaManager::GetStorageId(mPersistenceType, mASCIIOrigin, Client::IDB,
  1718                              mName, mDatabaseId);
  1719   MOZ_ASSERT(!mDatabaseId.IsEmpty());
  1721   return NS_OK;
  1724 nsresult
  1725 OpenDatabaseHelper::WaitForOpenAllowed()
  1727   NS_ASSERTION(mState == eCreated, "We've already been dispatched?");
  1728   NS_ASSERTION(NS_IsMainThread(), "All hell is about to break lose!");
  1730   mState = eOpenPending;
  1732   QuotaManager* quotaManager = QuotaManager::Get();
  1733   NS_ASSERTION(quotaManager, "This should never be null!");
  1735   return quotaManager->
  1736     WaitForOpenAllowed(OriginOrPatternString::FromOrigin(mASCIIOrigin),
  1737                        Nullable<PersistenceType>(mPersistenceType), mDatabaseId,
  1738                        this);
  1741 nsresult
  1742 OpenDatabaseHelper::Dispatch(nsIEventTarget* aTarget)
  1744   NS_ASSERTION(mState == eCreated || mState == eOpenPending,
  1745                "We've already been dispatched?");
  1747   mState = eDBWork;
  1749   return aTarget->Dispatch(this, NS_DISPATCH_NORMAL);
  1752 nsresult
  1753 OpenDatabaseHelper::DispatchToIOThread()
  1755   QuotaManager* quotaManager = QuotaManager::Get();
  1756   NS_ASSERTION(quotaManager, "This should never be null!");
  1758   return Dispatch(quotaManager->IOThread());
  1761 nsresult
  1762 OpenDatabaseHelper::RunImmediately()
  1764   NS_ASSERTION(mState == eCreated || mState == eOpenPending,
  1765                "We've already been dispatched?");
  1766   NS_ASSERTION(NS_FAILED(mResultCode),
  1767                "Should only be short-circuiting if we failed!");
  1768   NS_ASSERTION(NS_IsMainThread(), "All hell is about to break lose!");
  1770   mState = eFiringEvents;
  1772   return this->Run();
  1775 nsresult
  1776 OpenDatabaseHelper::DoDatabaseWork()
  1778   AssertIsOnIOThread();
  1779   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
  1781   PROFILER_LABEL("IndexedDB", "OpenDatabaseHelper::DoDatabaseWork");
  1783   mState = eFiringEvents; // In case we fail somewhere along the line.
  1785   if (QuotaManager::IsShuttingDown()) {
  1786     IDB_REPORT_INTERNAL_ERR();
  1787     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  1790   NS_ASSERTION(mOpenDBRequest, "This should never be null!");
  1792   // This will be null for non-window contexts.
  1793   nsPIDOMWindow* window = mOpenDBRequest->GetOwner();
  1795   AutoEnterWindow autoWindow(window);
  1797   nsCOMPtr<nsIFile> dbDirectory;
  1799   QuotaManager* quotaManager = QuotaManager::Get();
  1800   NS_ASSERTION(quotaManager, "This should never be null!");
  1802   nsresult rv =
  1803     quotaManager->EnsureOriginIsInitialized(mPersistenceType, mGroup,
  1804                                             mASCIIOrigin, mTrackingQuota,
  1805                                             getter_AddRefs(dbDirectory));
  1806   IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  1808   rv = dbDirectory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME));
  1809   IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  1811   bool exists;
  1812   rv = dbDirectory->Exists(&exists);
  1813   IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  1815   if (!exists) {
  1816     rv = dbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
  1817     IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  1819 #ifdef DEBUG
  1820   else {
  1821     bool isDirectory;
  1822     NS_ASSERTION(NS_SUCCEEDED(dbDirectory->IsDirectory(&isDirectory)) &&
  1823                 isDirectory, "Should have caught this earlier!");
  1825 #endif
  1827   nsAutoString filename;
  1828   rv = GetDatabaseFilename(mName, filename);
  1829   IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  1831   nsCOMPtr<nsIFile> dbFile;
  1832   rv = dbDirectory->Clone(getter_AddRefs(dbFile));
  1833   IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  1835   rv = dbFile->Append(filename + NS_LITERAL_STRING(".sqlite"));
  1836   IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  1838   rv = dbFile->GetPath(mDatabaseFilePath);
  1839   IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  1841   nsCOMPtr<nsIFile> fmDirectory;
  1842   rv = dbDirectory->Clone(getter_AddRefs(fmDirectory));
  1843   IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  1845   rv = fmDirectory->Append(filename);
  1846   IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  1848   nsCOMPtr<mozIStorageConnection> connection;
  1849   rv = CreateDatabaseConnection(dbFile, fmDirectory, mName, mPersistenceType,
  1850                                 mGroup, mASCIIOrigin,
  1851                                 getter_AddRefs(connection));
  1852   if (NS_FAILED(rv) &&
  1853       NS_ERROR_GET_MODULE(rv) != NS_ERROR_MODULE_DOM_INDEXEDDB) {
  1854     IDB_REPORT_INTERNAL_ERR();
  1855     rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  1857   NS_ENSURE_SUCCESS(rv, rv);
  1859   rv = IDBFactory::LoadDatabaseInformation(connection, mDatabaseId,
  1860                                            &mCurrentVersion, mObjectStores);
  1861   IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  1863   if (mForDeletion) {
  1864     mState = eDeletePending;
  1865     return NS_OK;
  1868   for (uint32_t i = 0; i < mObjectStores.Length(); i++) {
  1869     nsRefPtr<ObjectStoreInfo>& objectStoreInfo = mObjectStores[i];
  1870     for (uint32_t j = 0; j < objectStoreInfo->indexes.Length(); j++) {
  1871       IndexInfo& indexInfo = objectStoreInfo->indexes[j];
  1872       mLastIndexId = std::max(indexInfo.id, mLastIndexId);
  1874     mLastObjectStoreId = std::max(objectStoreInfo->id, mLastObjectStoreId);
  1877   // See if we need to do a VERSION_CHANGE transaction
  1879   // Optional version semantics.
  1880   if (!mRequestedVersion) {
  1881     // If the requested version was not specified and the database was created,
  1882     // treat it as if version 1 were requested.
  1883     if (mCurrentVersion == 0) {
  1884       mRequestedVersion = 1;
  1886     else {
  1887       // Otherwise, treat it as if the current version were requested.
  1888       mRequestedVersion = mCurrentVersion;
  1892   if (mCurrentVersion > mRequestedVersion) {
  1893     return NS_ERROR_DOM_INDEXEDDB_VERSION_ERR;
  1896   if (mCurrentVersion != mRequestedVersion) {
  1897     mState = eSetVersionPending;
  1900   IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
  1901   NS_ASSERTION(mgr, "This should never be null!");
  1903   nsRefPtr<FileManager> fileManager =
  1904     mgr->GetFileManager(mPersistenceType, mASCIIOrigin, mName);
  1905   if (!fileManager) {
  1906     fileManager = new FileManager(mPersistenceType, mGroup, mASCIIOrigin,
  1907                                   mPrivilege, mName);
  1909     rv = fileManager->Init(fmDirectory, connection);
  1910     IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  1912     mgr->AddFileManager(fileManager);
  1915   mFileManager = fileManager.forget();
  1917   return NS_OK;
  1920 // static
  1921 nsresult
  1922 OpenDatabaseHelper::CreateDatabaseConnection(
  1923                                         nsIFile* aDBFile,
  1924                                         nsIFile* aFMDirectory,
  1925                                         const nsAString& aName,
  1926                                         PersistenceType aPersistenceType,
  1927                                         const nsACString& aGroup,
  1928                                         const nsACString& aOrigin,
  1929                                         mozIStorageConnection** aConnection)
  1931   AssertIsOnIOThread();
  1932   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
  1934   PROFILER_LABEL("IndexedDB", "OpenDatabaseHelper::CreateDatabaseConnection");
  1936   nsresult rv;
  1937   bool exists;
  1939   if (IndexedDatabaseManager::InLowDiskSpaceMode()) {
  1940     rv = aDBFile->Exists(&exists);
  1941     NS_ENSURE_SUCCESS(rv, rv);
  1943     if (!exists) {
  1944       NS_WARNING("Refusing to create database because disk space is low!");
  1945       return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
  1949   nsCOMPtr<nsIFileURL> dbFileUrl =
  1950     IDBFactory::GetDatabaseFileURL(aDBFile, aPersistenceType, aGroup, aOrigin);
  1951   NS_ENSURE_TRUE(dbFileUrl, NS_ERROR_FAILURE);
  1953   nsCOMPtr<mozIStorageService> ss =
  1954     do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
  1955   NS_ENSURE_TRUE(ss, NS_ERROR_FAILURE);
  1957   nsCOMPtr<mozIStorageConnection> connection;
  1958   rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection));
  1959   if (rv == NS_ERROR_FILE_CORRUPTED) {
  1960     // If we're just opening the database during origin initialization, then
  1961     // we don't want to erase any files. The failure here will fail origin
  1962     // initialization too.
  1963     if (aName.IsVoid()) {
  1964       return rv;
  1967     // Nuke the database file.  The web services can recreate their data.
  1968     rv = aDBFile->Remove(false);
  1969     NS_ENSURE_SUCCESS(rv, rv);
  1971     rv = aFMDirectory->Exists(&exists);
  1972     NS_ENSURE_SUCCESS(rv, rv);
  1974     if (exists) {
  1975       bool isDirectory;
  1976       rv = aFMDirectory->IsDirectory(&isDirectory);
  1977       NS_ENSURE_SUCCESS(rv, rv);
  1978       IDB_ENSURE_TRUE(isDirectory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  1980       rv = aFMDirectory->Remove(true);
  1981       NS_ENSURE_SUCCESS(rv, rv);
  1984     rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection));
  1986   NS_ENSURE_SUCCESS(rv, rv);
  1988   rv = IDBFactory::SetDefaultPragmas(connection);
  1989   NS_ENSURE_SUCCESS(rv, rv);
  1991   rv = connection->EnableModule(NS_LITERAL_CSTRING("filesystem"));
  1992   NS_ENSURE_SUCCESS(rv, rv);
  1994   // Check to make sure that the database schema is correct.
  1995   int32_t schemaVersion;
  1996   rv = connection->GetSchemaVersion(&schemaVersion);
  1997   NS_ENSURE_SUCCESS(rv, rv);
  1999   // Unknown schema will fail origin initialization too
  2000   if (!schemaVersion && aName.IsVoid()) {
  2001     IDB_WARNING("Unable to open IndexedDB database, schema is not set!");
  2002     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  2005   if (schemaVersion > kSQLiteSchemaVersion) {
  2006     IDB_WARNING("Unable to open IndexedDB database, schema is too high!");
  2007     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  2010   bool vacuumNeeded = false;
  2012   if (schemaVersion != kSQLiteSchemaVersion) {
  2013 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
  2014     if (!schemaVersion) {
  2015       // Have to do this before opening a transaction.
  2016       rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  2017         // Turn on auto_vacuum mode to reclaim disk space on mobile devices.
  2018         "PRAGMA auto_vacuum = FULL; "
  2019       ));
  2020       if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
  2021         // mozstorage translates SQLITE_FULL to NS_ERROR_FILE_NO_DEVICE_SPACE,
  2022         // which we know better as NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR.
  2023         rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
  2025       NS_ENSURE_SUCCESS(rv, rv);
  2027 #endif
  2029     mozStorageTransaction transaction(connection, false,
  2030                                   mozIStorageConnection::TRANSACTION_IMMEDIATE);
  2032     if (!schemaVersion) {
  2033       // Brand new file, initialize our tables.
  2034       rv = CreateTables(connection);
  2035       NS_ENSURE_SUCCESS(rv, rv);
  2037       NS_ASSERTION(NS_SUCCEEDED(connection->GetSchemaVersion(&schemaVersion)) &&
  2038                    schemaVersion == kSQLiteSchemaVersion,
  2039                    "CreateTables set a bad schema version!");
  2041       nsCOMPtr<mozIStorageStatement> stmt;
  2042       nsresult rv = connection->CreateStatement(NS_LITERAL_CSTRING(
  2043         "INSERT INTO database (name) "
  2044         "VALUES (:name)"
  2045       ), getter_AddRefs(stmt));
  2046       IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  2048       rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), aName);
  2049       IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  2051       rv = stmt->Execute();
  2052       IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  2054     else  {
  2055       // This logic needs to change next time we change the schema!
  2056       static_assert(kSQLiteSchemaVersion == int32_t((14 << 4) + 0),
  2057                     "Need upgrade code from schema version increase.");
  2059       while (schemaVersion != kSQLiteSchemaVersion) {
  2060         if (schemaVersion == 4) {
  2061           rv = UpgradeSchemaFrom4To5(connection);
  2063         else if (schemaVersion == 5) {
  2064           rv = UpgradeSchemaFrom5To6(connection);
  2066         else if (schemaVersion == 6) {
  2067           rv = UpgradeSchemaFrom6To7(connection);
  2069         else if (schemaVersion == 7) {
  2070           rv = UpgradeSchemaFrom7To8(connection);
  2072         else if (schemaVersion == 8) {
  2073           rv = UpgradeSchemaFrom8To9_0(connection);
  2074           vacuumNeeded = true;
  2076         else if (schemaVersion == MakeSchemaVersion(9, 0)) {
  2077           rv = UpgradeSchemaFrom9_0To10_0(connection);
  2079         else if (schemaVersion == MakeSchemaVersion(10, 0)) {
  2080           rv = UpgradeSchemaFrom10_0To11_0(connection);
  2082         else if (schemaVersion == MakeSchemaVersion(11, 0)) {
  2083           rv = UpgradeSchemaFrom11_0To12_0(connection);
  2085         else if (schemaVersion == MakeSchemaVersion(12, 0)) {
  2086           rv = UpgradeSchemaFrom12_0To13_0(connection, &vacuumNeeded);
  2088         else if (schemaVersion == MakeSchemaVersion(13, 0)) {
  2089           rv = UpgradeSchemaFrom13_0To14_0(connection);
  2091         else {
  2092           NS_WARNING("Unable to open IndexedDB database, no upgrade path is "
  2093                      "available!");
  2094           IDB_REPORT_INTERNAL_ERR();
  2095           return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  2097         NS_ENSURE_SUCCESS(rv, rv);
  2099         rv = connection->GetSchemaVersion(&schemaVersion);
  2100         NS_ENSURE_SUCCESS(rv, rv);
  2103       NS_ASSERTION(schemaVersion == kSQLiteSchemaVersion, "Huh?!");
  2106     rv = transaction.Commit();
  2107     if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
  2108       // mozstorage translates SQLITE_FULL to NS_ERROR_FILE_NO_DEVICE_SPACE,
  2109       // which we know better as NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR.
  2110       rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
  2112     NS_ENSURE_SUCCESS(rv, rv);
  2115   if (vacuumNeeded) {
  2116     rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("VACUUM;"));
  2117     NS_ENSURE_SUCCESS(rv, rv);
  2120   connection.forget(aConnection);
  2121   return NS_OK;
  2124 nsresult
  2125 OpenDatabaseHelper::StartSetVersion()
  2127   NS_ASSERTION(mState == eSetVersionPending, "Why are we here?");
  2129   // In case we fail, fire error events
  2130   mState = eFiringEvents;
  2132   nsresult rv = EnsureSuccessResult();
  2133   NS_ENSURE_SUCCESS(rv, rv);
  2135   Sequence<nsString> storesToOpen;
  2136   nsRefPtr<IDBTransaction> transaction =
  2137     IDBTransaction::Create(mDatabase, storesToOpen,
  2138                            IDBTransaction::VERSION_CHANGE, true);
  2139   IDB_ENSURE_TRUE(transaction, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  2141   nsRefPtr<SetVersionHelper> helper =
  2142     new SetVersionHelper(transaction, mOpenDBRequest, this, mRequestedVersion,
  2143                          mCurrentVersion);
  2145   QuotaManager* quotaManager = QuotaManager::Get();
  2146   NS_ASSERTION(quotaManager, "This should never be null!");
  2148   rv = quotaManager->AcquireExclusiveAccess(
  2149              mDatabase, mDatabase->Origin(),
  2150              Nullable<PersistenceType>(mDatabase->Type()), helper,
  2151              &VersionChangeEventsRunnable::QueueVersionChange<SetVersionHelper>,
  2152              helper);
  2153   IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  2155   // The SetVersionHelper is responsible for dispatching us back to the
  2156   // main thread again and changing the state to eSetVersionCompleted.
  2157   mState = eSetVersionPending;
  2158   return NS_OK;
  2161 nsresult
  2162 OpenDatabaseHelper::StartDelete()
  2164   NS_ASSERTION(mState == eDeletePending, "Why are we here?");
  2166   // In case we fail, fire error events
  2167   mState = eFiringEvents;
  2169   nsresult rv = EnsureSuccessResult();
  2170   NS_ENSURE_SUCCESS(rv, rv);
  2172   nsRefPtr<DeleteDatabaseHelper> helper =
  2173     new DeleteDatabaseHelper(mOpenDBRequest, this, mCurrentVersion, mName,
  2174                              mGroup, mASCIIOrigin, mPersistenceType);
  2176   QuotaManager* quotaManager = QuotaManager::Get();
  2177   NS_ASSERTION(quotaManager, "This should never be null!");
  2179   rv = quotaManager->AcquireExclusiveAccess(
  2180          mDatabase, mDatabase->Origin(),
  2181          Nullable<PersistenceType>(mDatabase->Type()), helper,
  2182          &VersionChangeEventsRunnable::QueueVersionChange<DeleteDatabaseHelper>,
  2183          helper);
  2184   IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  2186   // The DeleteDatabaseHelper is responsible for dispatching us back to the
  2187   // main thread again and changing the state to eDeleteCompleted.
  2188   mState = eDeletePending;
  2189   return NS_OK;
  2192 NS_IMETHODIMP
  2193 OpenDatabaseHelper::Run()
  2195   NS_ASSERTION(mState != eCreated, "Dispatch was not called?!?");
  2197   if (NS_IsMainThread()) {
  2198     PROFILER_MAIN_THREAD_LABEL("IndexedDB", "OpenDatabaseHelper::Run");
  2200     if (mState == eOpenPending) {
  2201       if (NS_FAILED(mResultCode)) {
  2202         return RunImmediately();
  2205       return DispatchToIOThread();
  2208     // If we need to queue up a SetVersionHelper, do that here.
  2209     if (mState == eSetVersionPending) {
  2210       nsresult rv = StartSetVersion();
  2212       if (NS_SUCCEEDED(rv)) {
  2213         return rv;
  2216       SetError(rv);
  2217       // fall through and run the default error processing
  2219     else if (mState == eDeletePending) {
  2220       nsresult rv = StartDelete();
  2222       if (NS_SUCCEEDED(rv)) {
  2223         return rv;
  2226       SetError(rv);
  2227       // fall through and run the default error processing
  2230     // We've done whatever work we need to do on the DB thread, and any
  2231     // SetVersion/DeleteDatabase stuff is done by now.
  2232     NS_ASSERTION(mState == eFiringEvents ||
  2233                  mState == eSetVersionCompleted ||
  2234                  mState == eDeleteCompleted, "Why are we here?");
  2236     switch (mState) {
  2237       case eSetVersionCompleted: {
  2238         mState = eFiringEvents;
  2239         break;
  2242       case eDeleteCompleted: {
  2243         // Destroy the database now (we should have the only ref).
  2244         mDatabase = nullptr;
  2246         DatabaseInfo::Remove(mDatabaseId);
  2248         mState = eFiringEvents;
  2249         break;
  2252       case eFiringEvents: {
  2253         // Notify the request that we're done, but only if we didn't just
  2254         // finish a [SetVersion/DeleteDatabase]Helper.  In that case, the
  2255         // helper tells the request that it is done, and we avoid calling
  2256         // NotifyHelperCompleted twice.
  2258         nsresult rv = mOpenDBRequest->NotifyHelperCompleted(this);
  2259         if (NS_SUCCEEDED(mResultCode) && NS_FAILED(rv)) {
  2260           mResultCode = rv;
  2262         break;
  2265       default:
  2266         NS_NOTREACHED("Shouldn't get here!");
  2269     NS_ASSERTION(mState == eFiringEvents, "Why are we here?");
  2271     IDB_PROFILER_MARK("IndexedDB Request %llu: Running main thread "
  2272                       "response (rv = %lu)",
  2273                       "IDBRequest[%llu] MT Done",
  2274                       mRequest->GetSerialNumber(), mResultCode);
  2276     if (NS_FAILED(mResultCode)) {
  2277       DispatchErrorEvent();
  2278     } else {
  2279       DispatchSuccessEvent();
  2282     QuotaManager* quotaManager = QuotaManager::Get();
  2283     NS_ASSERTION(quotaManager, "This should never be null!");
  2285     quotaManager->
  2286       AllowNextSynchronizedOp(OriginOrPatternString::FromOrigin(mASCIIOrigin),
  2287                               Nullable<PersistenceType>(mPersistenceType),
  2288                               mDatabaseId);
  2290     ReleaseMainThreadObjects();
  2292     return NS_OK;
  2295   PROFILER_LABEL("IndexedDB", "OpenDatabaseHelper::Run");
  2297   // We're on the DB thread.
  2298   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
  2300   IDB_PROFILER_MARK("IndexedDB Request %llu: Beginning database work",
  2301                     "IDBRequest[%llu] DT Start", mRequest->GetSerialNumber());
  2303   NS_ASSERTION(mState == eDBWork, "Why are we here?");
  2304   mResultCode = DoDatabaseWork();
  2305   NS_ASSERTION(mState != eDBWork, "We should be doing something else now.");
  2307   IDB_PROFILER_MARK("IndexedDB Request %llu: Finished database work (rv = %lu)",
  2308                     "IDBRequest[%llu] DT Done", mRequest->GetSerialNumber(),
  2309                     mResultCode);
  2311   return NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL);
  2314 nsresult
  2315 OpenDatabaseHelper::EnsureSuccessResult()
  2317   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  2319   PROFILER_MAIN_THREAD_LABEL("IndexedDB",
  2320                              "OpenDatabaseHelper::EnsureSuccessResult");
  2322   nsRefPtr<DatabaseInfo> dbInfo;
  2323   if (DatabaseInfo::Get(mDatabaseId, getter_AddRefs(dbInfo))) {
  2325 #ifdef DEBUG
  2327       NS_ASSERTION(dbInfo->name == mName &&
  2328                    dbInfo->version == mCurrentVersion &&
  2329                    dbInfo->persistenceType == mPersistenceType &&
  2330                    dbInfo->id == mDatabaseId &&
  2331                    dbInfo->filePath == mDatabaseFilePath,
  2332                    "Metadata mismatch!");
  2334       uint32_t objectStoreCount = mObjectStores.Length();
  2335       for (uint32_t index = 0; index < objectStoreCount; index++) {
  2336         nsRefPtr<ObjectStoreInfo>& info = mObjectStores[index];
  2338         ObjectStoreInfo* otherInfo = dbInfo->GetObjectStore(info->name);
  2339         NS_ASSERTION(otherInfo, "ObjectStore not known!");
  2341         NS_ASSERTION(info->name == otherInfo->name &&
  2342                      info->id == otherInfo->id &&
  2343                      info->keyPath == otherInfo->keyPath,
  2344                      "Metadata mismatch!");
  2345         NS_ASSERTION(dbInfo->ContainsStoreName(info->name),
  2346                      "Object store names out of date!");
  2347         NS_ASSERTION(info->indexes.Length() == otherInfo->indexes.Length(),
  2348                      "Bad index length!");
  2350         uint32_t indexCount = info->indexes.Length();
  2351         for (uint32_t indexIndex = 0; indexIndex < indexCount; indexIndex++) {
  2352           const IndexInfo& indexInfo = info->indexes[indexIndex];
  2353           const IndexInfo& otherIndexInfo = otherInfo->indexes[indexIndex];
  2354           NS_ASSERTION(indexInfo.id == otherIndexInfo.id,
  2355                        "Bad index id!");
  2356           NS_ASSERTION(indexInfo.name == otherIndexInfo.name,
  2357                        "Bad index name!");
  2358           NS_ASSERTION(indexInfo.keyPath == otherIndexInfo.keyPath,
  2359                        "Bad index keyPath!");
  2360           NS_ASSERTION(indexInfo.unique == otherIndexInfo.unique,
  2361                        "Bad index unique value!");
  2365 #endif
  2368   else {
  2369     nsRefPtr<DatabaseInfo> newInfo(new DatabaseInfo());
  2371     newInfo->name = mName;
  2372     newInfo->group = mGroup;
  2373     newInfo->origin = mASCIIOrigin;
  2374     newInfo->persistenceType = mPersistenceType;
  2375     newInfo->id = mDatabaseId;
  2376     newInfo->filePath = mDatabaseFilePath;
  2378     if (!DatabaseInfo::Put(newInfo)) {
  2379       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  2382     newInfo.swap(dbInfo);
  2384     nsresult rv = IDBFactory::SetDatabaseMetadata(dbInfo, mCurrentVersion,
  2385                                                   mObjectStores);
  2386     IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  2388     NS_ASSERTION(mObjectStores.IsEmpty(), "Should have swapped!");
  2391   dbInfo->nextObjectStoreId = mLastObjectStoreId + 1;
  2392   dbInfo->nextIndexId = mLastIndexId + 1;
  2394   nsRefPtr<IDBDatabase> database =
  2395     IDBDatabase::Create(mOpenDBRequest, mOpenDBRequest->Factory(),
  2396                         dbInfo.forget(), mASCIIOrigin, mFileManager,
  2397                         mContentParent);
  2398   if (!database) {
  2399     IDB_REPORT_INTERNAL_ERR();
  2400     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  2403   NS_ASSERTION(!mDatabase, "Shouldn't have a database yet!");
  2404   mDatabase.swap(database);
  2406   return NS_OK;
  2409 nsresult
  2410 OpenDatabaseHelper::GetSuccessResult(JSContext* aCx,
  2411                                      JS::MutableHandle<JS::Value> aVal)
  2413   // Be careful not to load the database twice.
  2414   if (!mDatabase) {
  2415     nsresult rv = EnsureSuccessResult();
  2416     NS_ENSURE_SUCCESS(rv, rv);
  2419   return WrapNative(aCx, NS_ISUPPORTS_CAST(EventTarget*, mDatabase),
  2420                     aVal);
  2423 nsresult
  2424 OpenDatabaseHelper::NotifySetVersionFinished()
  2426   NS_ASSERTION(NS_IsMainThread(), "Wrong thread");
  2427   NS_ASSERTION(mState = eSetVersionPending, "How did we get here?");
  2429   // Allow transaction creation to proceed.
  2430   mDatabase->ExitSetVersionTransaction();
  2432   mState = eSetVersionCompleted;
  2434   // Dispatch ourself back to the main thread
  2435   return NS_DispatchToCurrentThread(this);
  2438 nsresult
  2439 OpenDatabaseHelper::NotifyDeleteFinished()
  2441   NS_ASSERTION(NS_IsMainThread(), "Wrong thread");
  2442   NS_ASSERTION(mState == eDeletePending, "How did we get here?");
  2444   mState = eDeleteCompleted;
  2446   // Dispatch ourself back to the main thread
  2447   return NS_DispatchToCurrentThread(this);
  2450 void
  2451 OpenDatabaseHelper::BlockDatabase()
  2453   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  2454   NS_ASSERTION(mDatabase, "This is going bad fast.");
  2456   mDatabase->EnterSetVersionTransaction();
  2459 void
  2460 OpenDatabaseHelper::DispatchSuccessEvent()
  2462   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  2464   PROFILER_MAIN_THREAD_LABEL("IndexedDB",
  2465                               "OpenDatabaseHelper::DispatchSuccessEvent");
  2467   nsRefPtr<nsIDOMEvent> event =
  2468     CreateGenericEvent(mOpenDBRequest, NS_LITERAL_STRING(SUCCESS_EVT_STR),
  2469                        eDoesNotBubble, eNotCancelable);
  2470   if (!event) {
  2471     NS_ERROR("Failed to create event!");
  2472     return;
  2475   bool dummy;
  2476   mOpenDBRequest->DispatchEvent(event, &dummy);
  2479 void
  2480 OpenDatabaseHelper::DispatchErrorEvent()
  2482   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  2484   PROFILER_MAIN_THREAD_LABEL("IndexedDB",
  2485                               "OpenDatabaseHelper::DispatchErrorEvent");
  2487   nsRefPtr<nsIDOMEvent> event =
  2488     CreateGenericEvent(mOpenDBRequest, NS_LITERAL_STRING(ERROR_EVT_STR),
  2489                        eDoesBubble, eCancelable);
  2490   if (!event) {
  2491     NS_ERROR("Failed to create event!");
  2492     return;
  2495   ErrorResult rv;
  2496   nsRefPtr<DOMError> error = mOpenDBRequest->GetError(rv);
  2498   NS_ASSERTION(!rv.Failed(), "This shouldn't be failing at this point!");
  2499   if (!error) {
  2500     mOpenDBRequest->SetError(mResultCode);
  2503   bool dummy;
  2504   mOpenDBRequest->DispatchEvent(event, &dummy);
  2507 void
  2508 OpenDatabaseHelper::ReleaseMainThreadObjects()
  2510   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  2512   mOpenDBRequest = nullptr;
  2513   mDatabase = nullptr;
  2515   HelperBase::ReleaseMainThreadObjects();
  2518 NS_IMPL_ISUPPORTS_INHERITED0(SetVersionHelper, AsyncConnectionHelper)
  2520 nsresult
  2521 SetVersionHelper::Init()
  2523   // Block transaction creation until we are done.
  2524   mOpenHelper->BlockDatabase();
  2526   return NS_OK;
  2529 nsresult
  2530 SetVersionHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
  2532   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
  2533   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
  2534   NS_ASSERTION(aConnection, "Passing a null connection!");
  2536   PROFILER_LABEL("IndexedDB", "SetVersionHelper::DoDatabaseWork");
  2538   nsCOMPtr<mozIStorageStatement> stmt;
  2539   nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
  2540     "UPDATE database "
  2541     "SET version = :version"
  2542   ), getter_AddRefs(stmt));
  2543   IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  2545   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("version"),
  2546                              mRequestedVersion);
  2547   IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  2549   if (NS_FAILED(stmt->Execute())) {
  2550     return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
  2553   return NS_OK;
  2556 nsresult
  2557 SetVersionHelper::GetSuccessResult(JSContext* aCx,
  2558                                    JS::MutableHandle<JS::Value> aVal)
  2560   DatabaseInfo* info = mDatabase->Info();
  2561   info->version = mRequestedVersion;
  2563   NS_ASSERTION(mTransaction, "Better have a transaction!");
  2565   mOpenRequest->SetTransaction(mTransaction);
  2567   return WrapNative(aCx, NS_ISUPPORTS_CAST(EventTarget*, mDatabase),
  2568                     aVal);
  2571 nsresult
  2572 SetVersionHelper::OnExclusiveAccessAcquired()
  2574   nsresult rv = DispatchToTransactionPool();
  2575   NS_ENSURE_SUCCESS(rv, rv);
  2577   return NS_OK;
  2580 // static
  2581 template <class T>
  2582 void
  2583 VersionChangeEventsRunnable::QueueVersionChange(
  2584                              nsTArray<nsCOMPtr<nsIOfflineStorage> >& aDatabases,
  2585                              void* aClosure)
  2587   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  2588   NS_ASSERTION(!aDatabases.IsEmpty(), "Why are we here?");
  2590   T* closure = static_cast<T*>(aClosure);
  2592   nsRefPtr<VersionChangeEventsRunnable> eventsRunnable =
  2593     new VersionChangeEventsRunnable(closure->mOpenHelper->Database(),
  2594                                     closure->mOpenRequest,
  2595                                     aDatabases,
  2596                                     closure->mCurrentVersion,
  2597                                     closure->RequestedVersion());
  2599   NS_DispatchToCurrentThread(eventsRunnable);
  2602 already_AddRefed<nsIDOMEvent>
  2603 SetVersionHelper::CreateSuccessEvent(mozilla::dom::EventTarget* aOwner)
  2605   NS_ASSERTION(mCurrentVersion < mRequestedVersion, "Huh?");
  2607   return IDBVersionChangeEvent::CreateUpgradeNeeded(aOwner,
  2608                                                     mCurrentVersion,
  2609                                                     mRequestedVersion);
  2612 nsresult
  2613 SetVersionHelper::NotifyTransactionPreComplete(IDBTransaction* aTransaction)
  2615   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  2616   NS_ASSERTION(aTransaction, "This is unexpected.");
  2617   NS_ASSERTION(mOpenRequest, "Why don't we have a request?");
  2619   return mOpenHelper->NotifySetVersionFinished();
  2622 nsresult
  2623 SetVersionHelper::NotifyTransactionPostComplete(IDBTransaction* aTransaction)
  2625   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  2626   NS_ASSERTION(aTransaction, "This is unexpected.");
  2627   NS_ASSERTION(mOpenRequest, "Why don't we have a request?");
  2629   // If we hit an error, the OpenDatabaseHelper needs to get that error too.
  2630   nsresult rv = GetResultCode();
  2631   if (NS_FAILED(rv)) {
  2632     mOpenHelper->SetError(rv);
  2635   // If the transaction was aborted, we should throw an error message.
  2636   if (aTransaction->IsAborted()) {
  2637     mOpenHelper->SetError(aTransaction->GetAbortCode());
  2640   mOpenRequest->SetTransaction(nullptr);
  2641   mOpenRequest = nullptr;
  2643   mOpenHelper = nullptr;
  2645   return rv;
  2648 NS_IMPL_ISUPPORTS_INHERITED0(DeleteDatabaseHelper, AsyncConnectionHelper);
  2650 nsresult
  2651 DeleteDatabaseHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
  2653   AssertIsOnIOThread();
  2654   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
  2655   NS_ASSERTION(!aConnection, "How did we get a connection here?");
  2657   PROFILER_LABEL("IndexedDB", "DeleteDatabaseHelper::DoDatabaseWork");
  2659   const StoragePrivilege& privilege = mOpenHelper->Privilege();
  2661   QuotaManager* quotaManager = QuotaManager::Get();
  2662   NS_ASSERTION(quotaManager, "This should never fail!");
  2664   nsCOMPtr<nsIFile> directory;
  2665   nsresult rv = quotaManager->GetDirectoryForOrigin(mPersistenceType,
  2666                                                     mASCIIOrigin,
  2667                                                     getter_AddRefs(directory));
  2668   IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  2670   NS_ASSERTION(directory, "What?");
  2672   rv = directory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME));
  2673   IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  2675   nsAutoString filename;
  2676   rv = GetDatabaseFilename(mName, filename);
  2677   IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  2679   nsCOMPtr<nsIFile> dbFile;
  2680   rv = directory->Clone(getter_AddRefs(dbFile));
  2681   IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  2683   rv = dbFile->Append(filename + NS_LITERAL_STRING(".sqlite"));
  2684   IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  2686   bool exists = false;
  2687   rv = dbFile->Exists(&exists);
  2688   IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  2690   if (exists) {
  2691     int64_t fileSize;
  2693     if (privilege != Chrome) {
  2694       rv = dbFile->GetFileSize(&fileSize);
  2695       IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  2698     rv = dbFile->Remove(false);
  2699     IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  2701     if (privilege != Chrome) {
  2702       QuotaManager* quotaManager = QuotaManager::Get();
  2703       NS_ASSERTION(quotaManager, "Shouldn't be null!");
  2705       quotaManager->DecreaseUsageForOrigin(mPersistenceType, mGroup,
  2706                                            mASCIIOrigin, fileSize);
  2710   nsCOMPtr<nsIFile> dbJournalFile;
  2711   rv = directory->Clone(getter_AddRefs(dbJournalFile));
  2712   IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  2714   rv = dbJournalFile->Append(filename + NS_LITERAL_STRING(".sqlite-journal"));
  2715   IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  2717   rv = dbJournalFile->Exists(&exists);
  2718   IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  2720   if (exists) {
  2721     rv = dbJournalFile->Remove(false);
  2722     IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  2725   nsCOMPtr<nsIFile> fmDirectory;
  2726   rv = directory->Clone(getter_AddRefs(fmDirectory));
  2727   IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  2729   rv = fmDirectory->Append(filename);
  2730   IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  2732   rv = fmDirectory->Exists(&exists);
  2733   IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  2735   if (exists) {
  2736     bool isDirectory;
  2737     rv = fmDirectory->IsDirectory(&isDirectory);
  2738     NS_ENSURE_SUCCESS(rv, rv);
  2739     IDB_ENSURE_TRUE(isDirectory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  2741     uint64_t usage = 0;
  2743     if (privilege != Chrome) {
  2744       rv = FileManager::GetUsage(fmDirectory, &usage);
  2745       IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  2748     rv = fmDirectory->Remove(true);
  2749     IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  2751     if (privilege != Chrome) {
  2752       QuotaManager* quotaManager = QuotaManager::Get();
  2753       NS_ASSERTION(quotaManager, "Shouldn't be null!");
  2755       quotaManager->DecreaseUsageForOrigin(mPersistenceType, mGroup,
  2756                                            mASCIIOrigin, usage);
  2760   IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
  2761   NS_ASSERTION(mgr, "This should never fail!");
  2763   mgr->InvalidateFileManager(mPersistenceType, mASCIIOrigin, mName);
  2765   return NS_OK;
  2768 nsresult
  2769 DeleteDatabaseHelper::GetSuccessResult(JSContext* aCx, JS::MutableHandle<JS::Value> aVal)
  2771   return NS_OK;
  2774 nsresult
  2775 DeleteDatabaseHelper::OnExclusiveAccessAcquired()
  2777   QuotaManager* quotaManager = QuotaManager::Get();
  2778   NS_ASSERTION(quotaManager, "We should definitely have a manager here");
  2780   nsresult rv = Dispatch(quotaManager->IOThread());
  2781   NS_ENSURE_SUCCESS(rv, rv);
  2783   return NS_OK;
  2786 nsresult
  2787 DeleteDatabaseHelper::Init()
  2789   // Note that there's no need to block the database here, since the page
  2790   // never gets to touch it, and all other databases must be closed.
  2792   return NS_OK;

mercurial