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