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