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