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