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