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