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