Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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;
1131 }
1133 class EncodeKeysFunction MOZ_FINAL : public mozIStorageFunction
1134 {
1135 public:
1136 NS_DECL_ISUPPORTS
1138 NS_IMETHOD
1139 OnFunctionCall(mozIStorageValueArray* aArguments,
1140 nsIVariant** aResult)
1141 {
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;
1151 }
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);
1162 }
1163 else if (type == mozIStorageStatement::VALUE_TYPE_TEXT) {
1164 nsString stringKey;
1165 aArguments->GetString(0, stringKey);
1166 key.SetFromString(stringKey);
1167 }
1168 else {
1169 NS_WARNING("Don't call me with the wrong type of arguments!");
1170 return NS_ERROR_UNEXPECTED;
1171 }
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;
1182 }
1183 };
1185 NS_IMPL_ISUPPORTS(EncodeKeysFunction, mozIStorageFunction)
1187 nsresult
1188 UpgradeSchemaFrom11_0To12_0(mozIStorageConnection* aConnection)
1189 {
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;
1401 }
1403 nsresult
1404 UpgradeSchemaFrom12_0To13_0(mozIStorageConnection* aConnection,
1405 bool* aVacuumNeeded)
1406 {
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;
1433 }
1435 nsresult
1436 UpgradeSchemaFrom13_0To14_0(mozIStorageConnection* aConnection)
1437 {
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;
1444 }
1446 class VersionChangeEventsRunnable;
1448 class SetVersionHelper : public AsyncConnectionHelper,
1449 public IDBTransactionListener,
1450 public AcquireListener
1451 {
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)
1464 {
1465 mTransaction->SetTransactionListener(this);
1466 }
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
1498 {
1499 return Success_NotSent;
1500 }
1502 virtual nsresult UnpackResponseFromParentProcess(
1503 const ResponseValue& aResponseValue)
1504 MOZ_OVERRIDE
1505 {
1506 MOZ_CRASH("Should never get here!");
1507 }
1509 uint64_t RequestedVersion() const
1510 {
1511 return mRequestedVersion;
1512 }
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
1524 {
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()
1547 {
1548 mOpenHelper = nullptr;
1549 mOpenRequest = nullptr;
1551 AsyncConnectionHelper::ReleaseMainThreadObjects();
1552 }
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()
1564 {
1565 mOpenHelper->NotifyDeleteFinished();
1566 }
1568 nsresult OnSuccess()
1569 {
1570 return mOpenHelper->NotifyDeleteFinished();
1571 }
1573 uint64_t RequestedVersion() const
1574 {
1575 return 0;
1576 }
1578 virtual ChildProcessSendResult
1579 SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE
1580 {
1581 return Success_NotSent;
1582 }
1584 virtual nsresult UnpackResponseFromParentProcess(
1585 const ResponseValue& aResponseValue)
1586 MOZ_OVERRIDE
1587 {
1588 MOZ_CRASH("Should never get here!");
1589 }
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
1606 {
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)
1618 {
1619 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1620 NS_ASSERTION(aRequestingDatabase, "Null pointer!");
1621 NS_ASSERTION(aRequest, "Null pointer!");
1623 mWaitingDatabases.SwapElements(aWaitingDatabases);
1624 }
1626 NS_IMETHOD Run()
1627 {
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;
1642 }
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;
1652 }
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;
1668 }
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);
1677 }
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;
1692 }
1693 }
1695 return NS_OK;
1696 }
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()
1716 {
1717 QuotaManager::GetStorageId(mPersistenceType, mASCIIOrigin, Client::IDB,
1718 mName, mDatabaseId);
1719 MOZ_ASSERT(!mDatabaseId.IsEmpty());
1721 return NS_OK;
1722 }
1724 nsresult
1725 OpenDatabaseHelper::WaitForOpenAllowed()
1726 {
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);
1739 }
1741 nsresult
1742 OpenDatabaseHelper::Dispatch(nsIEventTarget* aTarget)
1743 {
1744 NS_ASSERTION(mState == eCreated || mState == eOpenPending,
1745 "We've already been dispatched?");
1747 mState = eDBWork;
1749 return aTarget->Dispatch(this, NS_DISPATCH_NORMAL);
1750 }
1752 nsresult
1753 OpenDatabaseHelper::DispatchToIOThread()
1754 {
1755 QuotaManager* quotaManager = QuotaManager::Get();
1756 NS_ASSERTION(quotaManager, "This should never be null!");
1758 return Dispatch(quotaManager->IOThread());
1759 }
1761 nsresult
1762 OpenDatabaseHelper::RunImmediately()
1763 {
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();
1773 }
1775 nsresult
1776 OpenDatabaseHelper::DoDatabaseWork()
1777 {
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;
1788 }
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);
1818 }
1819 #ifdef DEBUG
1820 else {
1821 bool isDirectory;
1822 NS_ASSERTION(NS_SUCCEEDED(dbDirectory->IsDirectory(&isDirectory)) &&
1823 isDirectory, "Should have caught this earlier!");
1824 }
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;
1856 }
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;
1866 }
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);
1873 }
1874 mLastObjectStoreId = std::max(objectStoreInfo->id, mLastObjectStoreId);
1875 }
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;
1885 }
1886 else {
1887 // Otherwise, treat it as if the current version were requested.
1888 mRequestedVersion = mCurrentVersion;
1889 }
1890 }
1892 if (mCurrentVersion > mRequestedVersion) {
1893 return NS_ERROR_DOM_INDEXEDDB_VERSION_ERR;
1894 }
1896 if (mCurrentVersion != mRequestedVersion) {
1897 mState = eSetVersionPending;
1898 }
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);
1913 }
1915 mFileManager = fileManager.forget();
1917 return NS_OK;
1918 }
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)
1930 {
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;
1946 }
1947 }
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;
1965 }
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);
1982 }
1984 rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection));
1985 }
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;
2003 }
2005 if (schemaVersion > kSQLiteSchemaVersion) {
2006 IDB_WARNING("Unable to open IndexedDB database, schema is too high!");
2007 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
2008 }
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;
2024 }
2025 NS_ENSURE_SUCCESS(rv, rv);
2026 }
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);
2053 }
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);
2062 }
2063 else if (schemaVersion == 5) {
2064 rv = UpgradeSchemaFrom5To6(connection);
2065 }
2066 else if (schemaVersion == 6) {
2067 rv = UpgradeSchemaFrom6To7(connection);
2068 }
2069 else if (schemaVersion == 7) {
2070 rv = UpgradeSchemaFrom7To8(connection);
2071 }
2072 else if (schemaVersion == 8) {
2073 rv = UpgradeSchemaFrom8To9_0(connection);
2074 vacuumNeeded = true;
2075 }
2076 else if (schemaVersion == MakeSchemaVersion(9, 0)) {
2077 rv = UpgradeSchemaFrom9_0To10_0(connection);
2078 }
2079 else if (schemaVersion == MakeSchemaVersion(10, 0)) {
2080 rv = UpgradeSchemaFrom10_0To11_0(connection);
2081 }
2082 else if (schemaVersion == MakeSchemaVersion(11, 0)) {
2083 rv = UpgradeSchemaFrom11_0To12_0(connection);
2084 }
2085 else if (schemaVersion == MakeSchemaVersion(12, 0)) {
2086 rv = UpgradeSchemaFrom12_0To13_0(connection, &vacuumNeeded);
2087 }
2088 else if (schemaVersion == MakeSchemaVersion(13, 0)) {
2089 rv = UpgradeSchemaFrom13_0To14_0(connection);
2090 }
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;
2096 }
2097 NS_ENSURE_SUCCESS(rv, rv);
2099 rv = connection->GetSchemaVersion(&schemaVersion);
2100 NS_ENSURE_SUCCESS(rv, rv);
2101 }
2103 NS_ASSERTION(schemaVersion == kSQLiteSchemaVersion, "Huh?!");
2104 }
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;
2111 }
2112 NS_ENSURE_SUCCESS(rv, rv);
2113 }
2115 if (vacuumNeeded) {
2116 rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("VACUUM;"));
2117 NS_ENSURE_SUCCESS(rv, rv);
2118 }
2120 connection.forget(aConnection);
2121 return NS_OK;
2122 }
2124 nsresult
2125 OpenDatabaseHelper::StartSetVersion()
2126 {
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;
2159 }
2161 nsresult
2162 OpenDatabaseHelper::StartDelete()
2163 {
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;
2190 }
2192 NS_IMETHODIMP
2193 OpenDatabaseHelper::Run()
2194 {
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();
2203 }
2205 return DispatchToIOThread();
2206 }
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;
2214 }
2216 SetError(rv);
2217 // fall through and run the default error processing
2218 }
2219 else if (mState == eDeletePending) {
2220 nsresult rv = StartDelete();
2222 if (NS_SUCCEEDED(rv)) {
2223 return rv;
2224 }
2226 SetError(rv);
2227 // fall through and run the default error processing
2228 }
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;
2240 }
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;
2250 }
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;
2261 }
2262 break;
2263 }
2265 default:
2266 NS_NOTREACHED("Shouldn't get here!");
2267 }
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();
2280 }
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;
2293 }
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);
2312 }
2314 nsresult
2315 OpenDatabaseHelper::EnsureSuccessResult()
2316 {
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
2326 {
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!");
2362 }
2363 }
2364 }
2365 #endif
2367 }
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;
2380 }
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!");
2389 }
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;
2401 }
2403 NS_ASSERTION(!mDatabase, "Shouldn't have a database yet!");
2404 mDatabase.swap(database);
2406 return NS_OK;
2407 }
2409 nsresult
2410 OpenDatabaseHelper::GetSuccessResult(JSContext* aCx,
2411 JS::MutableHandle<JS::Value> aVal)
2412 {
2413 // Be careful not to load the database twice.
2414 if (!mDatabase) {
2415 nsresult rv = EnsureSuccessResult();
2416 NS_ENSURE_SUCCESS(rv, rv);
2417 }
2419 return WrapNative(aCx, NS_ISUPPORTS_CAST(EventTarget*, mDatabase),
2420 aVal);
2421 }
2423 nsresult
2424 OpenDatabaseHelper::NotifySetVersionFinished()
2425 {
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);
2436 }
2438 nsresult
2439 OpenDatabaseHelper::NotifyDeleteFinished()
2440 {
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);
2448 }
2450 void
2451 OpenDatabaseHelper::BlockDatabase()
2452 {
2453 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2454 NS_ASSERTION(mDatabase, "This is going bad fast.");
2456 mDatabase->EnterSetVersionTransaction();
2457 }
2459 void
2460 OpenDatabaseHelper::DispatchSuccessEvent()
2461 {
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;
2473 }
2475 bool dummy;
2476 mOpenDBRequest->DispatchEvent(event, &dummy);
2477 }
2479 void
2480 OpenDatabaseHelper::DispatchErrorEvent()
2481 {
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;
2493 }
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);
2501 }
2503 bool dummy;
2504 mOpenDBRequest->DispatchEvent(event, &dummy);
2505 }
2507 void
2508 OpenDatabaseHelper::ReleaseMainThreadObjects()
2509 {
2510 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2512 mOpenDBRequest = nullptr;
2513 mDatabase = nullptr;
2515 HelperBase::ReleaseMainThreadObjects();
2516 }
2518 NS_IMPL_ISUPPORTS_INHERITED0(SetVersionHelper, AsyncConnectionHelper)
2520 nsresult
2521 SetVersionHelper::Init()
2522 {
2523 // Block transaction creation until we are done.
2524 mOpenHelper->BlockDatabase();
2526 return NS_OK;
2527 }
2529 nsresult
2530 SetVersionHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
2531 {
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;
2551 }
2553 return NS_OK;
2554 }
2556 nsresult
2557 SetVersionHelper::GetSuccessResult(JSContext* aCx,
2558 JS::MutableHandle<JS::Value> aVal)
2559 {
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);
2569 }
2571 nsresult
2572 SetVersionHelper::OnExclusiveAccessAcquired()
2573 {
2574 nsresult rv = DispatchToTransactionPool();
2575 NS_ENSURE_SUCCESS(rv, rv);
2577 return NS_OK;
2578 }
2580 // static
2581 template <class T>
2582 void
2583 VersionChangeEventsRunnable::QueueVersionChange(
2584 nsTArray<nsCOMPtr<nsIOfflineStorage> >& aDatabases,
2585 void* aClosure)
2586 {
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);
2600 }
2602 already_AddRefed<nsIDOMEvent>
2603 SetVersionHelper::CreateSuccessEvent(mozilla::dom::EventTarget* aOwner)
2604 {
2605 NS_ASSERTION(mCurrentVersion < mRequestedVersion, "Huh?");
2607 return IDBVersionChangeEvent::CreateUpgradeNeeded(aOwner,
2608 mCurrentVersion,
2609 mRequestedVersion);
2610 }
2612 nsresult
2613 SetVersionHelper::NotifyTransactionPreComplete(IDBTransaction* aTransaction)
2614 {
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();
2620 }
2622 nsresult
2623 SetVersionHelper::NotifyTransactionPostComplete(IDBTransaction* aTransaction)
2624 {
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);
2633 }
2635 // If the transaction was aborted, we should throw an error message.
2636 if (aTransaction->IsAborted()) {
2637 mOpenHelper->SetError(aTransaction->GetAbortCode());
2638 }
2640 mOpenRequest->SetTransaction(nullptr);
2641 mOpenRequest = nullptr;
2643 mOpenHelper = nullptr;
2645 return rv;
2646 }
2648 NS_IMPL_ISUPPORTS_INHERITED0(DeleteDatabaseHelper, AsyncConnectionHelper);
2650 nsresult
2651 DeleteDatabaseHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
2652 {
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);
2696 }
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);
2707 }
2708 }
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);
2723 }
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);
2746 }
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);
2757 }
2758 }
2760 IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
2761 NS_ASSERTION(mgr, "This should never fail!");
2763 mgr->InvalidateFileManager(mPersistenceType, mASCIIOrigin, mName);
2765 return NS_OK;
2766 }
2768 nsresult
2769 DeleteDatabaseHelper::GetSuccessResult(JSContext* aCx, JS::MutableHandle<JS::Value> aVal)
2770 {
2771 return NS_OK;
2772 }
2774 nsresult
2775 DeleteDatabaseHelper::OnExclusiveAccessAcquired()
2776 {
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;
2784 }
2786 nsresult
2787 DeleteDatabaseHelper::Init()
2788 {
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;
2793 }