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