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