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