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