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