Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / content / browser / indexed_db / indexed_db_database.cc
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/indexed_db/indexed_db_database.h"
6
7 #include <math.h>
8 #include <set>
9
10 #include "base/auto_reset.h"
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "content/browser/indexed_db/indexed_db_connection.h"
17 #include "content/browser/indexed_db/indexed_db_cursor.h"
18 #include "content/browser/indexed_db/indexed_db_factory.h"
19 #include "content/browser/indexed_db/indexed_db_index_writer.h"
20 #include "content/browser/indexed_db/indexed_db_tracing.h"
21 #include "content/browser/indexed_db/indexed_db_transaction.h"
22 #include "content/common/indexed_db/indexed_db_key_path.h"
23 #include "content/common/indexed_db/indexed_db_key_range.h"
24 #include "third_party/WebKit/public/platform/WebIDBDatabaseException.h"
25
26 using base::ASCIIToUTF16;
27 using base::Int64ToString16;
28 using blink::WebIDBKeyTypeNumber;
29
30 namespace content {
31
32 // PendingOpenCall has a scoped_refptr<IndexedDBDatabaseCallbacks> because it
33 // isn't a connection yet.
34 class IndexedDBDatabase::PendingOpenCall {
35  public:
36   PendingOpenCall(scoped_refptr<IndexedDBCallbacks> callbacks,
37                   scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
38                   int64 transaction_id,
39                   int64 version)
40       : callbacks_(callbacks),
41         database_callbacks_(database_callbacks),
42         version_(version),
43         transaction_id_(transaction_id) {}
44   scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
45   scoped_refptr<IndexedDBDatabaseCallbacks> const database_callbacks() {
46     return database_callbacks_;
47   }
48   int64 version() const { return version_; }
49   int64 transaction_id() const { return transaction_id_; }
50
51  private:
52   scoped_refptr<IndexedDBCallbacks> callbacks_;
53   scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks_;
54   int64 version_;
55   const int64 transaction_id_;
56 };
57
58 // PendingUpgradeCall has a scoped_ptr<IndexedDBConnection> because it owns the
59 // in-progress connection.
60 class IndexedDBDatabase::PendingUpgradeCall {
61  public:
62   PendingUpgradeCall(scoped_refptr<IndexedDBCallbacks> callbacks,
63                      scoped_ptr<IndexedDBConnection> connection,
64                      int64 transaction_id,
65                      int64 version)
66       : callbacks_(callbacks),
67         connection_(connection.Pass()),
68         version_(version),
69         transaction_id_(transaction_id) {}
70   scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
71   // Takes ownership of the connection object.
72   scoped_ptr<IndexedDBConnection> ReleaseConnection() WARN_UNUSED_RESULT {
73     return connection_.Pass();
74   }
75   int64 version() const { return version_; }
76   int64 transaction_id() const { return transaction_id_; }
77
78  private:
79   scoped_refptr<IndexedDBCallbacks> callbacks_;
80   scoped_ptr<IndexedDBConnection> connection_;
81   int64 version_;
82   const int64 transaction_id_;
83 };
84
85 // PendingSuccessCall has a IndexedDBConnection* because the connection is now
86 // owned elsewhere, but we need to cancel the success call if that connection
87 // closes before it is sent.
88 class IndexedDBDatabase::PendingSuccessCall {
89  public:
90   PendingSuccessCall(scoped_refptr<IndexedDBCallbacks> callbacks,
91                      IndexedDBConnection* connection,
92                      int64 version)
93       : callbacks_(callbacks), connection_(connection), version_(version) {}
94   scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
95   IndexedDBConnection* connection() const { return connection_; }
96   int64 version() const { return version_; }
97
98  private:
99   scoped_refptr<IndexedDBCallbacks> callbacks_;
100   IndexedDBConnection* connection_;
101   int64 version_;
102 };
103
104 class IndexedDBDatabase::PendingDeleteCall {
105  public:
106   explicit PendingDeleteCall(scoped_refptr<IndexedDBCallbacks> callbacks)
107       : callbacks_(callbacks) {}
108   scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
109
110  private:
111   scoped_refptr<IndexedDBCallbacks> callbacks_;
112 };
113
114 scoped_refptr<IndexedDBDatabase> IndexedDBDatabase::Create(
115     const base::string16& name,
116     IndexedDBBackingStore* backing_store,
117     IndexedDBFactory* factory,
118     const Identifier& unique_identifier) {
119   scoped_refptr<IndexedDBDatabase> database =
120       new IndexedDBDatabase(name, backing_store, factory, unique_identifier);
121   if (!database->OpenInternal())
122     return 0;
123   return database;
124 }
125
126 namespace {
127 const base::string16::value_type kNoStringVersion[] = {0};
128 }
129
130 IndexedDBDatabase::IndexedDBDatabase(const base::string16& name,
131                                      IndexedDBBackingStore* backing_store,
132                                      IndexedDBFactory* factory,
133                                      const Identifier& unique_identifier)
134     : backing_store_(backing_store),
135       metadata_(name,
136                 kInvalidId,
137                 kNoStringVersion,
138                 IndexedDBDatabaseMetadata::NO_INT_VERSION,
139                 kInvalidId),
140       identifier_(unique_identifier),
141       factory_(factory) {
142   DCHECK(!metadata_.name.empty());
143 }
144
145 void IndexedDBDatabase::AddObjectStore(
146     const IndexedDBObjectStoreMetadata& object_store,
147     int64 new_max_object_store_id) {
148   DCHECK(metadata_.object_stores.find(object_store.id) ==
149          metadata_.object_stores.end());
150   if (new_max_object_store_id != IndexedDBObjectStoreMetadata::kInvalidId) {
151     DCHECK_LT(metadata_.max_object_store_id, new_max_object_store_id);
152     metadata_.max_object_store_id = new_max_object_store_id;
153   }
154   metadata_.object_stores[object_store.id] = object_store;
155 }
156
157 void IndexedDBDatabase::RemoveObjectStore(int64 object_store_id) {
158   DCHECK(metadata_.object_stores.find(object_store_id) !=
159          metadata_.object_stores.end());
160   metadata_.object_stores.erase(object_store_id);
161 }
162
163 void IndexedDBDatabase::AddIndex(int64 object_store_id,
164                                  const IndexedDBIndexMetadata& index,
165                                  int64 new_max_index_id) {
166   DCHECK(metadata_.object_stores.find(object_store_id) !=
167          metadata_.object_stores.end());
168   IndexedDBObjectStoreMetadata object_store =
169       metadata_.object_stores[object_store_id];
170
171   DCHECK(object_store.indexes.find(index.id) == object_store.indexes.end());
172   object_store.indexes[index.id] = index;
173   if (new_max_index_id != IndexedDBIndexMetadata::kInvalidId) {
174     DCHECK_LT(object_store.max_index_id, new_max_index_id);
175     object_store.max_index_id = new_max_index_id;
176   }
177   metadata_.object_stores[object_store_id] = object_store;
178 }
179
180 void IndexedDBDatabase::RemoveIndex(int64 object_store_id, int64 index_id) {
181   DCHECK(metadata_.object_stores.find(object_store_id) !=
182          metadata_.object_stores.end());
183   IndexedDBObjectStoreMetadata object_store =
184       metadata_.object_stores[object_store_id];
185
186   DCHECK(object_store.indexes.find(index_id) != object_store.indexes.end());
187   object_store.indexes.erase(index_id);
188   metadata_.object_stores[object_store_id] = object_store;
189 }
190
191 bool IndexedDBDatabase::OpenInternal() {
192   bool success = false;
193   bool ok = backing_store_->GetIDBDatabaseMetaData(
194       metadata_.name, &metadata_, &success);
195   DCHECK(success == (metadata_.id != kInvalidId)) << "success = " << success
196                                                   << " id = " << metadata_.id;
197   if (!ok)
198     return false;
199   if (success)
200     return backing_store_->GetObjectStores(metadata_.id,
201                                            &metadata_.object_stores);
202
203   return backing_store_->CreateIDBDatabaseMetaData(
204       metadata_.name, metadata_.version, metadata_.int_version, &metadata_.id);
205 }
206
207 IndexedDBDatabase::~IndexedDBDatabase() {
208   DCHECK(transactions_.empty());
209   DCHECK(pending_open_calls_.empty());
210   DCHECK(pending_delete_calls_.empty());
211 }
212
213 IndexedDBTransaction* IndexedDBDatabase::GetTransaction(
214     int64 transaction_id) const {
215   TransactionMap::const_iterator trans_iterator =
216       transactions_.find(transaction_id);
217   if (trans_iterator == transactions_.end())
218     return NULL;
219   return trans_iterator->second;
220 }
221
222 bool IndexedDBDatabase::ValidateObjectStoreId(int64 object_store_id) const {
223   if (!ContainsKey(metadata_.object_stores, object_store_id)) {
224     DLOG(ERROR) << "Invalid object_store_id";
225     return false;
226   }
227   return true;
228 }
229
230 bool IndexedDBDatabase::ValidateObjectStoreIdAndIndexId(int64 object_store_id,
231                                                         int64 index_id) const {
232   if (!ValidateObjectStoreId(object_store_id))
233     return false;
234   const IndexedDBObjectStoreMetadata& object_store_metadata =
235       metadata_.object_stores.find(object_store_id)->second;
236   if (!ContainsKey(object_store_metadata.indexes, index_id)) {
237     DLOG(ERROR) << "Invalid index_id";
238     return false;
239   }
240   return true;
241 }
242
243 bool IndexedDBDatabase::ValidateObjectStoreIdAndOptionalIndexId(
244     int64 object_store_id,
245     int64 index_id) const {
246   if (!ValidateObjectStoreId(object_store_id))
247     return false;
248   const IndexedDBObjectStoreMetadata& object_store_metadata =
249       metadata_.object_stores.find(object_store_id)->second;
250   if (index_id != IndexedDBIndexMetadata::kInvalidId &&
251       !ContainsKey(object_store_metadata.indexes, index_id)) {
252     DLOG(ERROR) << "Invalid index_id";
253     return false;
254   }
255   return true;
256 }
257
258 bool IndexedDBDatabase::ValidateObjectStoreIdAndNewIndexId(
259     int64 object_store_id,
260     int64 index_id) const {
261   if (!ValidateObjectStoreId(object_store_id))
262     return false;
263   const IndexedDBObjectStoreMetadata& object_store_metadata =
264       metadata_.object_stores.find(object_store_id)->second;
265   if (ContainsKey(object_store_metadata.indexes, index_id)) {
266     DLOG(ERROR) << "Invalid index_id";
267     return false;
268   }
269   return true;
270 }
271
272 void IndexedDBDatabase::CreateObjectStore(int64 transaction_id,
273                                           int64 object_store_id,
274                                           const base::string16& name,
275                                           const IndexedDBKeyPath& key_path,
276                                           bool auto_increment) {
277   IDB_TRACE("IndexedDBDatabase::CreateObjectStore");
278   IndexedDBTransaction* transaction = GetTransaction(transaction_id);
279   if (!transaction)
280     return;
281   DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
282
283   if (ContainsKey(metadata_.object_stores, object_store_id)) {
284     DLOG(ERROR) << "Invalid object_store_id";
285     return;
286   }
287
288   IndexedDBObjectStoreMetadata object_store_metadata(
289       name,
290       object_store_id,
291       key_path,
292       auto_increment,
293       IndexedDBDatabase::kMinimumIndexId);
294
295   transaction->ScheduleTask(
296       base::Bind(&IndexedDBDatabase::CreateObjectStoreOperation,
297                  this,
298                  object_store_metadata),
299       base::Bind(&IndexedDBDatabase::CreateObjectStoreAbortOperation,
300                  this,
301                  object_store_id));
302
303   AddObjectStore(object_store_metadata, object_store_id);
304 }
305
306 void IndexedDBDatabase::CreateObjectStoreOperation(
307     const IndexedDBObjectStoreMetadata& object_store_metadata,
308     IndexedDBTransaction* transaction) {
309   IDB_TRACE("IndexedDBDatabase::CreateObjectStoreOperation");
310   if (!backing_store_->CreateObjectStore(
311           transaction->BackingStoreTransaction(),
312           transaction->database()->id(),
313           object_store_metadata.id,
314           object_store_metadata.name,
315           object_store_metadata.key_path,
316           object_store_metadata.auto_increment)) {
317     transaction->Abort(IndexedDBDatabaseError(
318         blink::WebIDBDatabaseExceptionUnknownError,
319         ASCIIToUTF16("Internal error creating object store '") +
320             object_store_metadata.name + ASCIIToUTF16("'.")));
321     return;
322   }
323 }
324
325 void IndexedDBDatabase::DeleteObjectStore(int64 transaction_id,
326                                           int64 object_store_id) {
327   IDB_TRACE("IndexedDBDatabase::DeleteObjectStore");
328   IndexedDBTransaction* transaction = GetTransaction(transaction_id);
329   if (!transaction)
330     return;
331   DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
332
333   if (!ValidateObjectStoreId(object_store_id))
334     return;
335
336   const IndexedDBObjectStoreMetadata& object_store_metadata =
337       metadata_.object_stores[object_store_id];
338
339   transaction->ScheduleTask(
340       base::Bind(&IndexedDBDatabase::DeleteObjectStoreOperation,
341                  this,
342                  object_store_metadata),
343       base::Bind(&IndexedDBDatabase::DeleteObjectStoreAbortOperation,
344                  this,
345                  object_store_metadata));
346   RemoveObjectStore(object_store_id);
347 }
348
349 void IndexedDBDatabase::CreateIndex(int64 transaction_id,
350                                     int64 object_store_id,
351                                     int64 index_id,
352                                     const base::string16& name,
353                                     const IndexedDBKeyPath& key_path,
354                                     bool unique,
355                                     bool multi_entry) {
356   IDB_TRACE("IndexedDBDatabase::CreateIndex");
357   IndexedDBTransaction* transaction = GetTransaction(transaction_id);
358   if (!transaction)
359     return;
360   DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
361
362   if (!ValidateObjectStoreIdAndNewIndexId(object_store_id, index_id))
363     return;
364   const IndexedDBIndexMetadata index_metadata(
365       name, index_id, key_path, unique, multi_entry);
366
367   transaction->ScheduleTask(
368       base::Bind(&IndexedDBDatabase::CreateIndexOperation,
369                  this,
370                  object_store_id,
371                  index_metadata),
372       base::Bind(&IndexedDBDatabase::CreateIndexAbortOperation,
373                  this,
374                  object_store_id,
375                  index_id));
376
377   AddIndex(object_store_id, index_metadata, index_id);
378 }
379
380 void IndexedDBDatabase::CreateIndexOperation(
381     int64 object_store_id,
382     const IndexedDBIndexMetadata& index_metadata,
383     IndexedDBTransaction* transaction) {
384   IDB_TRACE("IndexedDBDatabase::CreateIndexOperation");
385   if (!backing_store_->CreateIndex(transaction->BackingStoreTransaction(),
386                                    transaction->database()->id(),
387                                    object_store_id,
388                                    index_metadata.id,
389                                    index_metadata.name,
390                                    index_metadata.key_path,
391                                    index_metadata.unique,
392                                    index_metadata.multi_entry)) {
393     base::string16 error_string =
394         ASCIIToUTF16("Internal error creating index '") +
395         index_metadata.name + ASCIIToUTF16("'.");
396     transaction->Abort(IndexedDBDatabaseError(
397         blink::WebIDBDatabaseExceptionUnknownError, error_string));
398     return;
399   }
400 }
401
402 void IndexedDBDatabase::CreateIndexAbortOperation(
403     int64 object_store_id,
404     int64 index_id,
405     IndexedDBTransaction* transaction) {
406   IDB_TRACE("IndexedDBDatabase::CreateIndexAbortOperation");
407   DCHECK(!transaction);
408   RemoveIndex(object_store_id, index_id);
409 }
410
411 void IndexedDBDatabase::DeleteIndex(int64 transaction_id,
412                                     int64 object_store_id,
413                                     int64 index_id) {
414   IDB_TRACE("IndexedDBDatabase::DeleteIndex");
415   IndexedDBTransaction* transaction = GetTransaction(transaction_id);
416   if (!transaction)
417     return;
418   DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
419
420   if (!ValidateObjectStoreIdAndIndexId(object_store_id, index_id))
421     return;
422   const IndexedDBIndexMetadata& index_metadata =
423       metadata_.object_stores[object_store_id].indexes[index_id];
424
425   transaction->ScheduleTask(
426       base::Bind(&IndexedDBDatabase::DeleteIndexOperation,
427                  this,
428                  object_store_id,
429                  index_metadata),
430       base::Bind(&IndexedDBDatabase::DeleteIndexAbortOperation,
431                  this,
432                  object_store_id,
433                  index_metadata));
434
435   RemoveIndex(object_store_id, index_id);
436 }
437
438 void IndexedDBDatabase::DeleteIndexOperation(
439     int64 object_store_id,
440     const IndexedDBIndexMetadata& index_metadata,
441     IndexedDBTransaction* transaction) {
442   IDB_TRACE("IndexedDBDatabase::DeleteIndexOperation");
443   bool ok = backing_store_->DeleteIndex(transaction->BackingStoreTransaction(),
444                                         transaction->database()->id(),
445                                         object_store_id,
446                                         index_metadata.id);
447   if (!ok) {
448     base::string16 error_string =
449         ASCIIToUTF16("Internal error deleting index '") +
450         index_metadata.name + ASCIIToUTF16("'.");
451     transaction->Abort(IndexedDBDatabaseError(
452         blink::WebIDBDatabaseExceptionUnknownError, error_string));
453   }
454 }
455
456 void IndexedDBDatabase::DeleteIndexAbortOperation(
457     int64 object_store_id,
458     const IndexedDBIndexMetadata& index_metadata,
459     IndexedDBTransaction* transaction) {
460   IDB_TRACE("IndexedDBDatabase::DeleteIndexAbortOperation");
461   DCHECK(!transaction);
462   AddIndex(object_store_id, index_metadata, IndexedDBIndexMetadata::kInvalidId);
463 }
464
465 void IndexedDBDatabase::Commit(int64 transaction_id) {
466   // The frontend suggests that we commit, but we may have previously initiated
467   // an abort, and so have disposed of the transaction. on_abort has already
468   // been dispatched to the frontend, so it will find out about that
469   // asynchronously.
470   IndexedDBTransaction* transaction = GetTransaction(transaction_id);
471   if (transaction)
472     transaction->Commit();
473 }
474
475 void IndexedDBDatabase::Abort(int64 transaction_id) {
476   // If the transaction is unknown, then it has already been aborted by the
477   // backend before this call so it is safe to ignore it.
478   IndexedDBTransaction* transaction = GetTransaction(transaction_id);
479   if (transaction)
480     transaction->Abort();
481 }
482
483 void IndexedDBDatabase::Abort(int64 transaction_id,
484                               const IndexedDBDatabaseError& error) {
485   // If the transaction is unknown, then it has already been aborted by the
486   // backend before this call so it is safe to ignore it.
487   IndexedDBTransaction* transaction = GetTransaction(transaction_id);
488   if (transaction)
489     transaction->Abort(error);
490 }
491
492 void IndexedDBDatabase::Get(int64 transaction_id,
493                             int64 object_store_id,
494                             int64 index_id,
495                             scoped_ptr<IndexedDBKeyRange> key_range,
496                             bool key_only,
497                             scoped_refptr<IndexedDBCallbacks> callbacks) {
498   IDB_TRACE("IndexedDBDatabase::Get");
499   IndexedDBTransaction* transaction = GetTransaction(transaction_id);
500   if (!transaction)
501     return;
502
503   if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id))
504     return;
505
506   transaction->ScheduleTask(base::Bind(
507       &IndexedDBDatabase::GetOperation,
508       this,
509       object_store_id,
510       index_id,
511       Passed(&key_range),
512       key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE,
513       callbacks));
514 }
515
516 void IndexedDBDatabase::GetOperation(
517     int64 object_store_id,
518     int64 index_id,
519     scoped_ptr<IndexedDBKeyRange> key_range,
520     indexed_db::CursorType cursor_type,
521     scoped_refptr<IndexedDBCallbacks> callbacks,
522     IndexedDBTransaction* transaction) {
523   IDB_TRACE("IndexedDBDatabase::GetOperation");
524
525   DCHECK(metadata_.object_stores.find(object_store_id) !=
526          metadata_.object_stores.end());
527   const IndexedDBObjectStoreMetadata& object_store_metadata =
528       metadata_.object_stores[object_store_id];
529
530   const IndexedDBKey* key;
531
532   scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
533   if (key_range->IsOnlyKey()) {
534     key = &key_range->lower();
535   } else {
536     if (index_id == IndexedDBIndexMetadata::kInvalidId) {
537       DCHECK_NE(cursor_type, indexed_db::CURSOR_KEY_ONLY);
538       // ObjectStore Retrieval Operation
539       backing_store_cursor = backing_store_->OpenObjectStoreCursor(
540           transaction->BackingStoreTransaction(),
541           id(),
542           object_store_id,
543           *key_range,
544           indexed_db::CURSOR_NEXT);
545     } else if (cursor_type == indexed_db::CURSOR_KEY_ONLY) {
546       // Index Value Retrieval Operation
547       backing_store_cursor = backing_store_->OpenIndexKeyCursor(
548           transaction->BackingStoreTransaction(),
549           id(),
550           object_store_id,
551           index_id,
552           *key_range,
553           indexed_db::CURSOR_NEXT);
554     } else {
555       // Index Referenced Value Retrieval Operation
556       backing_store_cursor = backing_store_->OpenIndexCursor(
557           transaction->BackingStoreTransaction(),
558           id(),
559           object_store_id,
560           index_id,
561           *key_range,
562           indexed_db::CURSOR_NEXT);
563     }
564
565     if (!backing_store_cursor) {
566       callbacks->OnSuccess();
567       return;
568     }
569
570     key = &backing_store_cursor->key();
571   }
572
573   scoped_ptr<IndexedDBKey> primary_key;
574   bool ok;
575   if (index_id == IndexedDBIndexMetadata::kInvalidId) {
576     // Object Store Retrieval Operation
577     std::string value;
578     ok = backing_store_->GetRecord(transaction->BackingStoreTransaction(),
579                                    id(),
580                                    object_store_id,
581                                    *key,
582                                    &value);
583     if (!ok) {
584       callbacks->OnError(
585           IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
586                                  "Internal error in GetRecord."));
587       return;
588     }
589
590     if (value.empty()) {
591       callbacks->OnSuccess();
592       return;
593     }
594
595     if (object_store_metadata.auto_increment &&
596         !object_store_metadata.key_path.IsNull()) {
597       callbacks->OnSuccess(&value, *key, object_store_metadata.key_path);
598       return;
599     }
600
601     callbacks->OnSuccess(&value);
602     return;
603   }
604
605   // From here we are dealing only with indexes.
606   ok = backing_store_->GetPrimaryKeyViaIndex(
607       transaction->BackingStoreTransaction(),
608       id(),
609       object_store_id,
610       index_id,
611       *key,
612       &primary_key);
613   if (!ok) {
614     callbacks->OnError(
615         IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
616                                "Internal error in GetPrimaryKeyViaIndex."));
617     return;
618   }
619   if (!primary_key) {
620     callbacks->OnSuccess();
621     return;
622   }
623   if (cursor_type == indexed_db::CURSOR_KEY_ONLY) {
624     // Index Value Retrieval Operation
625     callbacks->OnSuccess(*primary_key);
626     return;
627   }
628
629   // Index Referenced Value Retrieval Operation
630   std::string value;
631   ok = backing_store_->GetRecord(transaction->BackingStoreTransaction(),
632                                  id(),
633                                  object_store_id,
634                                  *primary_key,
635                                  &value);
636   if (!ok) {
637     callbacks->OnError(
638         IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
639                                "Internal error in GetRecord."));
640     return;
641   }
642
643   if (value.empty()) {
644     callbacks->OnSuccess();
645     return;
646   }
647   if (object_store_metadata.auto_increment &&
648       !object_store_metadata.key_path.IsNull()) {
649     callbacks->OnSuccess(&value, *primary_key, object_store_metadata.key_path);
650     return;
651   }
652   callbacks->OnSuccess(&value);
653 }
654
655 static scoped_ptr<IndexedDBKey> GenerateKey(
656     IndexedDBBackingStore* backing_store,
657     IndexedDBTransaction* transaction,
658     int64 database_id,
659     int64 object_store_id) {
660   const int64 max_generator_value =
661       9007199254740992LL;  // Maximum integer storable as ECMAScript number.
662   int64 current_number;
663   bool ok = backing_store->GetKeyGeneratorCurrentNumber(
664       transaction->BackingStoreTransaction(),
665       database_id,
666       object_store_id,
667       &current_number);
668   if (!ok) {
669     LOG(ERROR) << "Failed to GetKeyGeneratorCurrentNumber";
670     return make_scoped_ptr(new IndexedDBKey());
671   }
672   if (current_number < 0 || current_number > max_generator_value)
673     return make_scoped_ptr(new IndexedDBKey());
674
675   return make_scoped_ptr(new IndexedDBKey(current_number, WebIDBKeyTypeNumber));
676 }
677
678 static bool UpdateKeyGenerator(IndexedDBBackingStore* backing_store,
679                                IndexedDBTransaction* transaction,
680                                int64 database_id,
681                                int64 object_store_id,
682                                const IndexedDBKey& key,
683                                bool check_current) {
684   DCHECK_EQ(WebIDBKeyTypeNumber, key.type());
685   return backing_store->MaybeUpdateKeyGeneratorCurrentNumber(
686       transaction->BackingStoreTransaction(),
687       database_id,
688       object_store_id,
689       static_cast<int64>(floor(key.number())) + 1,
690       check_current);
691 }
692
693 struct IndexedDBDatabase::PutOperationParams {
694   PutOperationParams() {}
695   int64 object_store_id;
696   std::string value;
697   scoped_ptr<IndexedDBKey> key;
698   IndexedDBDatabase::PutMode put_mode;
699   scoped_refptr<IndexedDBCallbacks> callbacks;
700   std::vector<IndexKeys> index_keys;
701
702  private:
703   DISALLOW_COPY_AND_ASSIGN(PutOperationParams);
704 };
705
706 void IndexedDBDatabase::Put(int64 transaction_id,
707                             int64 object_store_id,
708                             std::string* value,
709                             scoped_ptr<IndexedDBKey> key,
710                             PutMode put_mode,
711                             scoped_refptr<IndexedDBCallbacks> callbacks,
712                             const std::vector<IndexKeys>& index_keys) {
713   IDB_TRACE("IndexedDBDatabase::Put");
714   IndexedDBTransaction* transaction = GetTransaction(transaction_id);
715   if (!transaction)
716     return;
717   DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY);
718
719   if (!ValidateObjectStoreId(object_store_id))
720     return;
721
722   DCHECK(key);
723   scoped_ptr<PutOperationParams> params(new PutOperationParams());
724   params->object_store_id = object_store_id;
725   params->value.swap(*value);
726   params->key = key.Pass();
727   params->put_mode = put_mode;
728   params->callbacks = callbacks;
729   params->index_keys = index_keys;
730   transaction->ScheduleTask(base::Bind(
731       &IndexedDBDatabase::PutOperation, this, base::Passed(&params)));
732 }
733
734 void IndexedDBDatabase::PutOperation(scoped_ptr<PutOperationParams> params,
735                                      IndexedDBTransaction* transaction) {
736   IDB_TRACE("IndexedDBDatabase::PutOperation");
737   DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY);
738   bool key_was_generated = false;
739
740   DCHECK(metadata_.object_stores.find(params->object_store_id) !=
741          metadata_.object_stores.end());
742   const IndexedDBObjectStoreMetadata& object_store =
743       metadata_.object_stores[params->object_store_id];
744   DCHECK(object_store.auto_increment || params->key->IsValid());
745
746   scoped_ptr<IndexedDBKey> key;
747   if (params->put_mode != IndexedDBDatabase::CURSOR_UPDATE &&
748       object_store.auto_increment && !params->key->IsValid()) {
749     scoped_ptr<IndexedDBKey> auto_inc_key = GenerateKey(
750         backing_store_.get(), transaction, id(), params->object_store_id);
751     key_was_generated = true;
752     if (!auto_inc_key->IsValid()) {
753       params->callbacks->OnError(
754           IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionConstraintError,
755                                  "Maximum key generator value reached."));
756       return;
757     }
758     key = auto_inc_key.Pass();
759   } else {
760     key = params->key.Pass();
761   }
762
763   DCHECK(key->IsValid());
764
765   IndexedDBBackingStore::RecordIdentifier record_identifier;
766   if (params->put_mode == IndexedDBDatabase::ADD_ONLY) {
767     bool found = false;
768     bool ok = backing_store_->KeyExistsInObjectStore(
769         transaction->BackingStoreTransaction(),
770         id(),
771         params->object_store_id,
772         *key,
773         &record_identifier,
774         &found);
775     if (!ok) {
776       params->callbacks->OnError(
777           IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
778                                  "Internal error checking key existence."));
779       return;
780     }
781     if (found) {
782       params->callbacks->OnError(
783           IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionConstraintError,
784                                  "Key already exists in the object store."));
785       return;
786     }
787   }
788
789   ScopedVector<IndexWriter> index_writers;
790   base::string16 error_message;
791   bool obeys_constraints = false;
792   bool backing_store_success = MakeIndexWriters(transaction,
793                                                 backing_store_.get(),
794                                                 id(),
795                                                 object_store,
796                                                 *key,
797                                                 key_was_generated,
798                                                 params->index_keys,
799                                                 &index_writers,
800                                                 &error_message,
801                                                 &obeys_constraints);
802   if (!backing_store_success) {
803     params->callbacks->OnError(IndexedDBDatabaseError(
804         blink::WebIDBDatabaseExceptionUnknownError,
805         "Internal error: backing store error updating index keys."));
806     return;
807   }
808   if (!obeys_constraints) {
809     params->callbacks->OnError(IndexedDBDatabaseError(
810         blink::WebIDBDatabaseExceptionConstraintError, error_message));
811     return;
812   }
813
814   // Before this point, don't do any mutation. After this point, rollback the
815   // transaction in case of error.
816   backing_store_success =
817       backing_store_->PutRecord(transaction->BackingStoreTransaction(),
818                                 id(),
819                                 params->object_store_id,
820                                 *key,
821                                 params->value,
822                                 &record_identifier);
823   if (!backing_store_success) {
824     params->callbacks->OnError(IndexedDBDatabaseError(
825         blink::WebIDBDatabaseExceptionUnknownError,
826         "Internal error: backing store error performing put/add."));
827     return;
828   }
829
830   for (size_t i = 0; i < index_writers.size(); ++i) {
831     IndexWriter* index_writer = index_writers[i];
832     index_writer->WriteIndexKeys(record_identifier,
833                                  backing_store_.get(),
834                                  transaction->BackingStoreTransaction(),
835                                  id(),
836                                  params->object_store_id);
837   }
838
839   if (object_store.auto_increment &&
840       params->put_mode != IndexedDBDatabase::CURSOR_UPDATE &&
841       key->type() == WebIDBKeyTypeNumber) {
842     bool ok = UpdateKeyGenerator(backing_store_.get(),
843                                  transaction,
844                                  id(),
845                                  params->object_store_id,
846                                  *key,
847                                  !key_was_generated);
848     if (!ok) {
849       params->callbacks->OnError(
850           IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
851                                  "Internal error updating key generator."));
852       return;
853     }
854   }
855
856   params->callbacks->OnSuccess(*key);
857 }
858
859 void IndexedDBDatabase::SetIndexKeys(int64 transaction_id,
860                                      int64 object_store_id,
861                                      scoped_ptr<IndexedDBKey> primary_key,
862                                      const std::vector<IndexKeys>& index_keys) {
863   IDB_TRACE("IndexedDBDatabase::SetIndexKeys");
864   IndexedDBTransaction* transaction = GetTransaction(transaction_id);
865   if (!transaction)
866     return;
867   DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
868
869   // TODO(alecflett): This method could be asynchronous, but we need to
870   // evaluate if it's worth the extra complexity.
871   IndexedDBBackingStore::RecordIdentifier record_identifier;
872   bool found = false;
873   bool ok = backing_store_->KeyExistsInObjectStore(
874       transaction->BackingStoreTransaction(),
875       metadata_.id,
876       object_store_id,
877       *primary_key,
878       &record_identifier,
879       &found);
880   if (!ok) {
881     transaction->Abort(
882         IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
883                                "Internal error setting index keys."));
884     return;
885   }
886   if (!found) {
887     transaction->Abort(IndexedDBDatabaseError(
888         blink::WebIDBDatabaseExceptionUnknownError,
889         "Internal error setting index keys for object store."));
890     return;
891   }
892
893   ScopedVector<IndexWriter> index_writers;
894   base::string16 error_message;
895   bool obeys_constraints = false;
896   DCHECK(metadata_.object_stores.find(object_store_id) !=
897          metadata_.object_stores.end());
898   const IndexedDBObjectStoreMetadata& object_store_metadata =
899       metadata_.object_stores[object_store_id];
900   bool backing_store_success = MakeIndexWriters(transaction,
901                                                 backing_store_,
902                                                 id(),
903                                                 object_store_metadata,
904                                                 *primary_key,
905                                                 false,
906                                                 index_keys,
907                                                 &index_writers,
908                                                 &error_message,
909                                                 &obeys_constraints);
910   if (!backing_store_success) {
911     transaction->Abort(IndexedDBDatabaseError(
912         blink::WebIDBDatabaseExceptionUnknownError,
913         "Internal error: backing store error updating index keys."));
914     return;
915   }
916   if (!obeys_constraints) {
917     transaction->Abort(IndexedDBDatabaseError(
918         blink::WebIDBDatabaseExceptionConstraintError, error_message));
919     return;
920   }
921
922   for (size_t i = 0; i < index_writers.size(); ++i) {
923     IndexWriter* index_writer = index_writers[i];
924     index_writer->WriteIndexKeys(record_identifier,
925                                  backing_store_,
926                                  transaction->BackingStoreTransaction(),
927                                  id(),
928                                  object_store_id);
929   }
930 }
931
932 void IndexedDBDatabase::SetIndexesReady(int64 transaction_id,
933                                         int64,
934                                         const std::vector<int64>& index_ids) {
935   IDB_TRACE("IndexedDBDatabase::SetIndexesReady");
936   IndexedDBTransaction* transaction = GetTransaction(transaction_id);
937   if (!transaction)
938     return;
939   DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
940
941   transaction->ScheduleTask(
942       IndexedDBDatabase::PREEMPTIVE_TASK,
943       base::Bind(&IndexedDBDatabase::SetIndexesReadyOperation,
944                  this,
945                  index_ids.size()));
946 }
947
948 void IndexedDBDatabase::SetIndexesReadyOperation(
949     size_t index_count,
950     IndexedDBTransaction* transaction) {
951   IDB_TRACE("IndexedDBDatabase::SetIndexesReadyOperation");
952   for (size_t i = 0; i < index_count; ++i)
953     transaction->DidCompletePreemptiveEvent();
954 }
955
956 struct IndexedDBDatabase::OpenCursorOperationParams {
957   OpenCursorOperationParams() {}
958   int64 object_store_id;
959   int64 index_id;
960   scoped_ptr<IndexedDBKeyRange> key_range;
961   indexed_db::CursorDirection direction;
962   indexed_db::CursorType cursor_type;
963   IndexedDBDatabase::TaskType task_type;
964   scoped_refptr<IndexedDBCallbacks> callbacks;
965
966  private:
967   DISALLOW_COPY_AND_ASSIGN(OpenCursorOperationParams);
968 };
969
970 void IndexedDBDatabase::OpenCursor(
971     int64 transaction_id,
972     int64 object_store_id,
973     int64 index_id,
974     scoped_ptr<IndexedDBKeyRange> key_range,
975     indexed_db::CursorDirection direction,
976     bool key_only,
977     TaskType task_type,
978     scoped_refptr<IndexedDBCallbacks> callbacks) {
979   IDB_TRACE("IndexedDBDatabase::OpenCursor");
980   IndexedDBTransaction* transaction = GetTransaction(transaction_id);
981   if (!transaction)
982     return;
983
984   if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id))
985     return;
986
987   scoped_ptr<OpenCursorOperationParams> params(new OpenCursorOperationParams());
988   params->object_store_id = object_store_id;
989   params->index_id = index_id;
990   params->key_range = key_range.Pass();
991   params->direction = direction;
992   params->cursor_type =
993       key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE;
994   params->task_type = task_type;
995   params->callbacks = callbacks;
996   transaction->ScheduleTask(base::Bind(
997       &IndexedDBDatabase::OpenCursorOperation, this, base::Passed(&params)));
998 }
999
1000 void IndexedDBDatabase::OpenCursorOperation(
1001     scoped_ptr<OpenCursorOperationParams> params,
1002     IndexedDBTransaction* transaction) {
1003   IDB_TRACE("IndexedDBDatabase::OpenCursorOperation");
1004
1005   // The frontend has begun indexing, so this pauses the transaction
1006   // until the indexing is complete. This can't happen any earlier
1007   // because we don't want to switch to early mode in case multiple
1008   // indexes are being created in a row, with Put()'s in between.
1009   if (params->task_type == IndexedDBDatabase::PREEMPTIVE_TASK)
1010     transaction->AddPreemptiveEvent();
1011
1012   scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
1013   if (params->index_id == IndexedDBIndexMetadata::kInvalidId) {
1014     if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) {
1015       DCHECK_EQ(params->task_type, IndexedDBDatabase::NORMAL_TASK);
1016       backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor(
1017           transaction->BackingStoreTransaction(),
1018           id(),
1019           params->object_store_id,
1020           *params->key_range,
1021           params->direction);
1022     } else {
1023       backing_store_cursor = backing_store_->OpenObjectStoreCursor(
1024           transaction->BackingStoreTransaction(),
1025           id(),
1026           params->object_store_id,
1027           *params->key_range,
1028         params->direction);
1029     }
1030   } else {
1031     DCHECK_EQ(params->task_type, IndexedDBDatabase::NORMAL_TASK);
1032     if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) {
1033       backing_store_cursor = backing_store_->OpenIndexKeyCursor(
1034           transaction->BackingStoreTransaction(),
1035           id(),
1036           params->object_store_id,
1037           params->index_id,
1038           *params->key_range,
1039           params->direction);
1040     } else {
1041       backing_store_cursor = backing_store_->OpenIndexCursor(
1042           transaction->BackingStoreTransaction(),
1043           id(),
1044           params->object_store_id,
1045           params->index_id,
1046           *params->key_range,
1047           params->direction);
1048     }
1049   }
1050
1051   if (!backing_store_cursor) {
1052     params->callbacks->OnSuccess(static_cast<std::string*>(NULL));
1053     return;
1054   }
1055
1056   scoped_refptr<IndexedDBCursor> cursor =
1057       new IndexedDBCursor(backing_store_cursor.Pass(),
1058                           params->cursor_type,
1059                           params->task_type,
1060                           transaction);
1061   params->callbacks->OnSuccess(
1062       cursor, cursor->key(), cursor->primary_key(), cursor->Value());
1063 }
1064
1065 void IndexedDBDatabase::Count(int64 transaction_id,
1066                               int64 object_store_id,
1067                               int64 index_id,
1068                               scoped_ptr<IndexedDBKeyRange> key_range,
1069                               scoped_refptr<IndexedDBCallbacks> callbacks) {
1070   IDB_TRACE("IndexedDBDatabase::Count");
1071   IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1072   if (!transaction)
1073     return;
1074
1075   if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id))
1076     return;
1077
1078   transaction->ScheduleTask(base::Bind(&IndexedDBDatabase::CountOperation,
1079                                        this,
1080                                        object_store_id,
1081                                        index_id,
1082                                        base::Passed(&key_range),
1083                                        callbacks));
1084 }
1085
1086 void IndexedDBDatabase::CountOperation(
1087     int64 object_store_id,
1088     int64 index_id,
1089     scoped_ptr<IndexedDBKeyRange> key_range,
1090     scoped_refptr<IndexedDBCallbacks> callbacks,
1091     IndexedDBTransaction* transaction) {
1092   IDB_TRACE("IndexedDBDatabase::CountOperation");
1093   uint32 count = 0;
1094   scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
1095
1096   if (index_id == IndexedDBIndexMetadata::kInvalidId) {
1097     backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor(
1098         transaction->BackingStoreTransaction(),
1099         id(),
1100         object_store_id,
1101         *key_range,
1102         indexed_db::CURSOR_NEXT);
1103   } else {
1104     backing_store_cursor = backing_store_->OpenIndexKeyCursor(
1105         transaction->BackingStoreTransaction(),
1106         id(),
1107         object_store_id,
1108         index_id,
1109         *key_range,
1110         indexed_db::CURSOR_NEXT);
1111   }
1112   if (!backing_store_cursor) {
1113     callbacks->OnSuccess(count);
1114     return;
1115   }
1116
1117   do {
1118     ++count;
1119   } while (backing_store_cursor->Continue());
1120
1121   callbacks->OnSuccess(count);
1122 }
1123
1124 void IndexedDBDatabase::DeleteRange(
1125     int64 transaction_id,
1126     int64 object_store_id,
1127     scoped_ptr<IndexedDBKeyRange> key_range,
1128     scoped_refptr<IndexedDBCallbacks> callbacks) {
1129   IDB_TRACE("IndexedDBDatabase::DeleteRange");
1130   IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1131   if (!transaction)
1132     return;
1133   DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY);
1134
1135   if (!ValidateObjectStoreId(object_store_id))
1136     return;
1137
1138   transaction->ScheduleTask(base::Bind(&IndexedDBDatabase::DeleteRangeOperation,
1139                                        this,
1140                                        object_store_id,
1141                                        base::Passed(&key_range),
1142                                        callbacks));
1143 }
1144
1145 void IndexedDBDatabase::DeleteRangeOperation(
1146     int64 object_store_id,
1147     scoped_ptr<IndexedDBKeyRange> key_range,
1148     scoped_refptr<IndexedDBCallbacks> callbacks,
1149     IndexedDBTransaction* transaction) {
1150   IDB_TRACE("IndexedDBDatabase::DeleteRangeOperation");
1151   scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor =
1152       backing_store_->OpenObjectStoreCursor(
1153           transaction->BackingStoreTransaction(),
1154           id(),
1155           object_store_id,
1156           *key_range,
1157           indexed_db::CURSOR_NEXT);
1158   if (backing_store_cursor) {
1159     do {
1160       if (!backing_store_->DeleteRecord(
1161               transaction->BackingStoreTransaction(),
1162               id(),
1163               object_store_id,
1164               backing_store_cursor->record_identifier())) {
1165         callbacks->OnError(
1166             IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
1167                                    "Internal error deleting data in range"));
1168         return;
1169       }
1170     } while (backing_store_cursor->Continue());
1171   }
1172
1173   callbacks->OnSuccess();
1174 }
1175
1176 void IndexedDBDatabase::Clear(int64 transaction_id,
1177                               int64 object_store_id,
1178                               scoped_refptr<IndexedDBCallbacks> callbacks) {
1179   IDB_TRACE("IndexedDBDatabase::Clear");
1180   IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1181   if (!transaction)
1182     return;
1183   DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY);
1184
1185   if (!ValidateObjectStoreId(object_store_id))
1186     return;
1187
1188   transaction->ScheduleTask(base::Bind(
1189       &IndexedDBDatabase::ClearOperation, this, object_store_id, callbacks));
1190 }
1191
1192 void IndexedDBDatabase::ClearOperation(
1193     int64 object_store_id,
1194     scoped_refptr<IndexedDBCallbacks> callbacks,
1195     IndexedDBTransaction* transaction) {
1196   IDB_TRACE("IndexedDBDatabase::ObjectStoreClearOperation");
1197   if (!backing_store_->ClearObjectStore(
1198           transaction->BackingStoreTransaction(), id(), object_store_id)) {
1199     callbacks->OnError(
1200         IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
1201                                "Internal error clearing object store"));
1202     return;
1203   }
1204   callbacks->OnSuccess();
1205 }
1206
1207 void IndexedDBDatabase::DeleteObjectStoreOperation(
1208     const IndexedDBObjectStoreMetadata& object_store_metadata,
1209     IndexedDBTransaction* transaction) {
1210   IDB_TRACE("IndexedDBDatabase::DeleteObjectStoreOperation");
1211   bool ok =
1212       backing_store_->DeleteObjectStore(transaction->BackingStoreTransaction(),
1213                                         transaction->database()->id(),
1214                                         object_store_metadata.id);
1215   if (!ok) {
1216     base::string16 error_string =
1217         ASCIIToUTF16("Internal error deleting object store '") +
1218         object_store_metadata.name + ASCIIToUTF16("'.");
1219     transaction->Abort(IndexedDBDatabaseError(
1220         blink::WebIDBDatabaseExceptionUnknownError, error_string));
1221   }
1222 }
1223
1224 void IndexedDBDatabase::VersionChangeOperation(
1225     int64 version,
1226     scoped_refptr<IndexedDBCallbacks> callbacks,
1227     scoped_ptr<IndexedDBConnection> connection,
1228     IndexedDBTransaction* transaction) {
1229   IDB_TRACE("IndexedDBDatabase::VersionChangeOperation");
1230   int64 old_version = metadata_.int_version;
1231   DCHECK_GT(version, old_version);
1232   metadata_.int_version = version;
1233   if (!backing_store_->UpdateIDBDatabaseIntVersion(
1234           transaction->BackingStoreTransaction(),
1235           id(),
1236           metadata_.int_version)) {
1237     IndexedDBDatabaseError error(
1238         blink::WebIDBDatabaseExceptionUnknownError,
1239         ASCIIToUTF16(
1240             "Internal error writing data to stable storage when "
1241             "updating version."));
1242     callbacks->OnError(error);
1243     transaction->Abort(error);
1244     return;
1245   }
1246   DCHECK(!pending_second_half_open_);
1247   pending_second_half_open_.reset(
1248       new PendingSuccessCall(callbacks, connection.get(), version));
1249   callbacks->OnUpgradeNeeded(old_version, connection.Pass(), metadata());
1250 }
1251
1252 void IndexedDBDatabase::TransactionFinished(IndexedDBTransaction* transaction,
1253                                             bool committed) {
1254   DCHECK(transactions_.find(transaction->id()) != transactions_.end());
1255   DCHECK_EQ(transactions_[transaction->id()], transaction);
1256   transactions_.erase(transaction->id());
1257
1258   if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) {
1259     if (pending_second_half_open_) {
1260       if (committed) {
1261         DCHECK_EQ(pending_second_half_open_->version(), metadata_.int_version);
1262         DCHECK(metadata_.id != kInvalidId);
1263
1264         // Connection was already minted for OnUpgradeNeeded callback.
1265         scoped_ptr<IndexedDBConnection> connection;
1266         pending_second_half_open_->callbacks()->OnSuccess(connection.Pass(),
1267                                                           this->metadata());
1268       } else {
1269         pending_second_half_open_->callbacks()->OnError(
1270             IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError,
1271                                    "Version change transaction was aborted in "
1272                                    "upgradeneeded event handler."));
1273       }
1274       pending_second_half_open_.reset();
1275     }
1276
1277     // Connection queue is now unblocked.
1278     ProcessPendingCalls();
1279   }
1280 }
1281
1282 void IndexedDBDatabase::TransactionCommitFailed() {
1283   // Factory may be null in unit tests.
1284   if (!factory_)
1285     return;
1286   factory_->HandleBackingStoreFailure(backing_store_->origin_url());
1287 }
1288
1289 size_t IndexedDBDatabase::ConnectionCount() const {
1290   // This does not include pending open calls, as those should not block version
1291   // changes and deletes.
1292   return connections_.size();
1293 }
1294
1295 size_t IndexedDBDatabase::PendingOpenCount() const {
1296   return pending_open_calls_.size();
1297 }
1298
1299 size_t IndexedDBDatabase::PendingUpgradeCount() const {
1300   return pending_run_version_change_transaction_call_ ? 1 : 0;
1301 }
1302
1303 size_t IndexedDBDatabase::RunningUpgradeCount() const {
1304   return pending_second_half_open_ ? 1 : 0;
1305 }
1306
1307 size_t IndexedDBDatabase::PendingDeleteCount() const {
1308   return pending_delete_calls_.size();
1309 }
1310
1311 void IndexedDBDatabase::ProcessPendingCalls() {
1312   if (pending_run_version_change_transaction_call_ && ConnectionCount() == 1) {
1313     DCHECK(pending_run_version_change_transaction_call_->version() >
1314            metadata_.int_version);
1315     scoped_ptr<PendingUpgradeCall> pending_call =
1316         pending_run_version_change_transaction_call_.Pass();
1317     RunVersionChangeTransactionFinal(pending_call->callbacks(),
1318                                      pending_call->ReleaseConnection(),
1319                                      pending_call->transaction_id(),
1320                                      pending_call->version());
1321     DCHECK_EQ(1u, ConnectionCount());
1322     // Fall through would be a no-op, since transaction must complete
1323     // asynchronously.
1324     DCHECK(IsDeleteDatabaseBlocked());
1325     DCHECK(IsOpenConnectionBlocked());
1326     return;
1327   }
1328
1329   if (!IsDeleteDatabaseBlocked()) {
1330     PendingDeleteCallList pending_delete_calls;
1331     pending_delete_calls_.swap(pending_delete_calls);
1332     while (!pending_delete_calls.empty()) {
1333       // Only the first delete call will delete the database, but each must fire
1334       // callbacks.
1335       scoped_ptr<PendingDeleteCall> pending_delete_call(
1336           pending_delete_calls.front());
1337       pending_delete_calls.pop_front();
1338       DeleteDatabaseFinal(pending_delete_call->callbacks());
1339     }
1340     // delete_database_final should never re-queue calls.
1341     DCHECK(pending_delete_calls_.empty());
1342     // Fall through when complete, as pending opens may be unblocked.
1343   }
1344
1345   if (!IsOpenConnectionBlocked()) {
1346     PendingOpenCallList pending_open_calls;
1347     pending_open_calls_.swap(pending_open_calls);
1348     while (!pending_open_calls.empty()) {
1349       scoped_ptr<PendingOpenCall> pending_open_call(pending_open_calls.front());
1350       pending_open_calls.pop_front();
1351       OpenConnection(pending_open_call->callbacks(),
1352                      pending_open_call->database_callbacks(),
1353                      pending_open_call->transaction_id(),
1354                      pending_open_call->version());
1355     }
1356   }
1357 }
1358
1359 void IndexedDBDatabase::CreateTransaction(
1360     int64 transaction_id,
1361     IndexedDBConnection* connection,
1362     const std::vector<int64>& object_store_ids,
1363     uint16 mode) {
1364
1365   DCHECK(connections_.count(connection));
1366   DCHECK(transactions_.find(transaction_id) == transactions_.end());
1367   if (transactions_.find(transaction_id) != transactions_.end())
1368     return;
1369
1370   // The transaction will add itself to this database's coordinator, which
1371   // manages the lifetime of the object.
1372   TransactionCreated(new IndexedDBTransaction(
1373       transaction_id,
1374       connection->callbacks(),
1375       std::set<int64>(object_store_ids.begin(), object_store_ids.end()),
1376       static_cast<indexed_db::TransactionMode>(mode),
1377       this,
1378       new IndexedDBBackingStore::Transaction(backing_store_)));
1379 }
1380
1381 void IndexedDBDatabase::TransactionCreated(IndexedDBTransaction* transaction) {
1382   transactions_[transaction->id()] = transaction;
1383 }
1384
1385 bool IndexedDBDatabase::IsOpenConnectionBlocked() const {
1386   return !pending_delete_calls_.empty() ||
1387          transaction_coordinator_.IsRunningVersionChangeTransaction() ||
1388          pending_run_version_change_transaction_call_;
1389 }
1390
1391 void IndexedDBDatabase::OpenConnection(
1392     scoped_refptr<IndexedDBCallbacks> callbacks,
1393     scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
1394     int64 transaction_id,
1395     int64 version) {
1396   DCHECK(backing_store_);
1397
1398   // TODO(jsbell): Should have a priority queue so that higher version
1399   // requests are processed first. http://crbug.com/225850
1400   if (IsOpenConnectionBlocked()) {
1401     // The backing store only detects data loss when it is first opened. The
1402     // presence of existing connections means we didn't even check for data loss
1403     // so there'd better not be any.
1404     DCHECK_NE(blink::WebIDBDataLossTotal, callbacks->data_loss());
1405     pending_open_calls_.push_back(new PendingOpenCall(
1406         callbacks, database_callbacks, transaction_id, version));
1407     return;
1408   }
1409
1410   if (metadata_.id == kInvalidId) {
1411     // The database was deleted then immediately re-opened; OpenInternal()
1412     // recreates it in the backing store.
1413     if (OpenInternal()) {
1414       DCHECK_EQ(IndexedDBDatabaseMetadata::NO_INT_VERSION,
1415                 metadata_.int_version);
1416     } else {
1417       base::string16 message;
1418       if (version == IndexedDBDatabaseMetadata::NO_INT_VERSION) {
1419         message = ASCIIToUTF16(
1420             "Internal error opening database with no version specified.");
1421       } else {
1422         message =
1423             ASCIIToUTF16("Internal error opening database with version ") +
1424             Int64ToString16(version);
1425       }
1426       callbacks->OnError(IndexedDBDatabaseError(
1427           blink::WebIDBDatabaseExceptionUnknownError, message));
1428       return;
1429     }
1430   }
1431
1432   // We infer that the database didn't exist from its lack of either type of
1433   // version.
1434   bool is_new_database =
1435       metadata_.version == kNoStringVersion &&
1436       metadata_.int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION;
1437
1438   scoped_ptr<IndexedDBConnection> connection(
1439       new IndexedDBConnection(this, database_callbacks));
1440
1441   if (version == IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION) {
1442     // For unit tests only - skip upgrade steps. Calling from script with
1443     // DEFAULT_INT_VERSION throws exception.
1444     // TODO(jsbell): DCHECK that not in unit tests.
1445     DCHECK(is_new_database);
1446     connections_.insert(connection.get());
1447     callbacks->OnSuccess(connection.Pass(), this->metadata());
1448     return;
1449   }
1450
1451   if (version == IndexedDBDatabaseMetadata::NO_INT_VERSION) {
1452     if (!is_new_database) {
1453       connections_.insert(connection.get());
1454       callbacks->OnSuccess(connection.Pass(), this->metadata());
1455       return;
1456     }
1457     // Spec says: If no version is specified and no database exists, set
1458     // database version to 1.
1459     version = 1;
1460   }
1461
1462   if (version > metadata_.int_version) {
1463     connections_.insert(connection.get());
1464     RunVersionChangeTransaction(
1465         callbacks, connection.Pass(), transaction_id, version);
1466     return;
1467   }
1468   if (version < metadata_.int_version) {
1469     callbacks->OnError(IndexedDBDatabaseError(
1470         blink::WebIDBDatabaseExceptionVersionError,
1471         ASCIIToUTF16("The requested version (") + Int64ToString16(version) +
1472             ASCIIToUTF16(") is less than the existing version (") +
1473             Int64ToString16(metadata_.int_version) + ASCIIToUTF16(").")));
1474     return;
1475   }
1476   DCHECK_EQ(version, metadata_.int_version);
1477   connections_.insert(connection.get());
1478   callbacks->OnSuccess(connection.Pass(), this->metadata());
1479 }
1480
1481 void IndexedDBDatabase::RunVersionChangeTransaction(
1482     scoped_refptr<IndexedDBCallbacks> callbacks,
1483     scoped_ptr<IndexedDBConnection> connection,
1484     int64 transaction_id,
1485     int64 requested_version) {
1486
1487   DCHECK(callbacks);
1488   DCHECK(connections_.count(connection.get()));
1489   if (ConnectionCount() > 1) {
1490     DCHECK_NE(blink::WebIDBDataLossTotal, callbacks->data_loss());
1491     // Front end ensures the event is not fired at connections that have
1492     // close_pending set.
1493     for (ConnectionSet::const_iterator it = connections_.begin();
1494          it != connections_.end();
1495          ++it) {
1496       if (*it != connection.get()) {
1497         (*it)->callbacks()->OnVersionChange(metadata_.int_version,
1498                                             requested_version);
1499       }
1500     }
1501     // TODO(jsbell): Remove the call to OnBlocked and instead wait
1502     // until the frontend tells us that all the "versionchange" events
1503     // have been delivered.  http://crbug.com/100123
1504     callbacks->OnBlocked(metadata_.int_version);
1505
1506     DCHECK(!pending_run_version_change_transaction_call_);
1507     pending_run_version_change_transaction_call_.reset(new PendingUpgradeCall(
1508         callbacks, connection.Pass(), transaction_id, requested_version));
1509     return;
1510   }
1511   RunVersionChangeTransactionFinal(
1512       callbacks, connection.Pass(), transaction_id, requested_version);
1513 }
1514
1515 void IndexedDBDatabase::RunVersionChangeTransactionFinal(
1516     scoped_refptr<IndexedDBCallbacks> callbacks,
1517     scoped_ptr<IndexedDBConnection> connection,
1518     int64 transaction_id,
1519     int64 requested_version) {
1520
1521   std::vector<int64> object_store_ids;
1522   CreateTransaction(transaction_id,
1523                     connection.get(),
1524                     object_store_ids,
1525                     indexed_db::TRANSACTION_VERSION_CHANGE);
1526
1527   transactions_[transaction_id]
1528       ->ScheduleTask(base::Bind(&IndexedDBDatabase::VersionChangeOperation,
1529                                 this,
1530                                 requested_version,
1531                                 callbacks,
1532                                 base::Passed(&connection)),
1533                      base::Bind(&IndexedDBDatabase::VersionChangeAbortOperation,
1534                                 this,
1535                                 metadata_.version,
1536                                 metadata_.int_version));
1537
1538   DCHECK(!pending_second_half_open_);
1539 }
1540
1541 void IndexedDBDatabase::DeleteDatabase(
1542     scoped_refptr<IndexedDBCallbacks> callbacks) {
1543
1544   if (IsDeleteDatabaseBlocked()) {
1545     for (ConnectionSet::const_iterator it = connections_.begin();
1546          it != connections_.end();
1547          ++it) {
1548       // Front end ensures the event is not fired at connections that have
1549       // close_pending set.
1550       (*it)->callbacks()->OnVersionChange(
1551           metadata_.int_version, IndexedDBDatabaseMetadata::NO_INT_VERSION);
1552     }
1553     // TODO(jsbell): Only fire OnBlocked if there are open
1554     // connections after the VersionChangeEvents are received, not
1555     // just set up to fire.  http://crbug.com/100123
1556     callbacks->OnBlocked(metadata_.int_version);
1557     pending_delete_calls_.push_back(new PendingDeleteCall(callbacks));
1558     return;
1559   }
1560   DeleteDatabaseFinal(callbacks);
1561 }
1562
1563 bool IndexedDBDatabase::IsDeleteDatabaseBlocked() const {
1564   return !!ConnectionCount();
1565 }
1566
1567 void IndexedDBDatabase::DeleteDatabaseFinal(
1568     scoped_refptr<IndexedDBCallbacks> callbacks) {
1569   DCHECK(!IsDeleteDatabaseBlocked());
1570   DCHECK(backing_store_);
1571   if (!backing_store_->DeleteDatabase(metadata_.name)) {
1572     callbacks->OnError(
1573         IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
1574                                "Internal error deleting database."));
1575     return;
1576   }
1577   metadata_.version = kNoStringVersion;
1578   metadata_.id = kInvalidId;
1579   metadata_.int_version = IndexedDBDatabaseMetadata::NO_INT_VERSION;
1580   metadata_.object_stores.clear();
1581   callbacks->OnSuccess();
1582   if (factory_)
1583     factory_->DatabaseDeleted(identifier_);
1584 }
1585
1586 void IndexedDBDatabase::ForceClose() {
1587   // IndexedDBConnection::ForceClose() may delete this database, so hold ref.
1588   scoped_refptr<IndexedDBDatabase> protect(this);
1589   ConnectionSet::const_iterator it = connections_.begin();
1590   while (it != connections_.end()) {
1591     IndexedDBConnection* connection = *it++;
1592     connection->ForceClose();
1593   }
1594   DCHECK(connections_.empty());
1595 }
1596
1597 void IndexedDBDatabase::Close(IndexedDBConnection* connection, bool forced) {
1598   DCHECK(connections_.count(connection));
1599   DCHECK(connection->IsConnected());
1600   DCHECK(connection->database() == this);
1601
1602   // Abort outstanding transactions from the closing connection. This
1603   // can not happen if the close is requested by the connection itself
1604   // as the front-end defers the close until all transactions are
1605   // complete, but can occur on process termination or forced close.
1606   {
1607     TransactionMap transactions(transactions_);
1608     for (TransactionMap::const_iterator it = transactions.begin(),
1609                                         end = transactions.end();
1610          it != end;
1611          ++it) {
1612       if (it->second->connection() == connection->callbacks())
1613         it->second->Abort(
1614             IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
1615                                    "Connection is closing."));
1616     }
1617   }
1618
1619   connections_.erase(connection);
1620   if (pending_second_half_open_ &&
1621       pending_second_half_open_->connection() == connection) {
1622     pending_second_half_open_->callbacks()->OnError(
1623         IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError,
1624                                "The connection was closed."));
1625     pending_second_half_open_.reset();
1626   }
1627
1628   ProcessPendingCalls();
1629
1630   // TODO(jsbell): Add a test for the pending_open_calls_ cases below.
1631   if (!ConnectionCount() && !pending_open_calls_.size() &&
1632       !pending_delete_calls_.size()) {
1633     DCHECK(transactions_.empty());
1634
1635     const GURL origin_url = backing_store_->origin_url();
1636     backing_store_ = NULL;
1637
1638     // factory_ should only be null in unit tests.
1639     // TODO(jsbell): DCHECK(factory_ || !in_unit_tests) - somehow.
1640     if (factory_) {
1641       factory_->ReleaseDatabase(identifier_, forced);
1642       factory_ = NULL;
1643     }
1644   }
1645 }
1646
1647 void IndexedDBDatabase::CreateObjectStoreAbortOperation(
1648     int64 object_store_id,
1649     IndexedDBTransaction* transaction) {
1650   IDB_TRACE("IndexedDBDatabase::CreateObjectStoreAbortOperation");
1651   DCHECK(!transaction);
1652   RemoveObjectStore(object_store_id);
1653 }
1654
1655 void IndexedDBDatabase::DeleteObjectStoreAbortOperation(
1656     const IndexedDBObjectStoreMetadata& object_store_metadata,
1657     IndexedDBTransaction* transaction) {
1658   IDB_TRACE("IndexedDBDatabase::DeleteObjectStoreAbortOperation");
1659   DCHECK(!transaction);
1660   AddObjectStore(object_store_metadata,
1661                  IndexedDBObjectStoreMetadata::kInvalidId);
1662 }
1663
1664 void IndexedDBDatabase::VersionChangeAbortOperation(
1665     const base::string16& previous_version,
1666     int64 previous_int_version,
1667     IndexedDBTransaction* transaction) {
1668   IDB_TRACE("IndexedDBDatabase::VersionChangeAbortOperation");
1669   DCHECK(!transaction);
1670   metadata_.version = previous_version;
1671   metadata_.int_version = previous_int_version;
1672 }
1673
1674 }  // namespace content