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