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