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