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