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 ¤t_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(¶ms)));
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 ¶ms->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(¶ms)));
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