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