[Mac] Implement Ambient Light API
[chromium-blink-merge.git] / content / browser / indexed_db / indexed_db_database.cc
blob97dc1d7ca07dd0610606f0c05d1923344d154c9e
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.
5 #include "content/browser/indexed_db/indexed_db_database.h"
7 #include <math.h>
8 #include <set>
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 "storage/browser/blob/blob_data_handle.h"
30 #include "third_party/WebKit/public/platform/WebIDBDatabaseException.h"
31 #include "third_party/leveldatabase/env_chromium.h"
33 using base::ASCIIToUTF16;
34 using base::Int64ToString16;
35 using blink::WebIDBKeyTypeNumber;
37 namespace content {
39 // PendingUpgradeCall has a scoped_ptr<IndexedDBConnection> because it owns the
40 // in-progress connection.
41 class IndexedDBDatabase::PendingUpgradeCall {
42 public:
43 PendingUpgradeCall(scoped_refptr<IndexedDBCallbacks> callbacks,
44 scoped_ptr<IndexedDBConnection> connection,
45 int64 transaction_id,
46 int64 version)
47 : callbacks_(callbacks),
48 connection_(connection.Pass()),
49 version_(version),
50 transaction_id_(transaction_id) {}
51 scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
52 // Takes ownership of the connection object.
53 scoped_ptr<IndexedDBConnection> ReleaseConnection() WARN_UNUSED_RESULT {
54 return connection_.Pass();
56 int64 version() const { return version_; }
57 int64 transaction_id() const { return transaction_id_; }
59 private:
60 scoped_refptr<IndexedDBCallbacks> callbacks_;
61 scoped_ptr<IndexedDBConnection> connection_;
62 int64 version_;
63 const int64 transaction_id_;
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:
71 PendingSuccessCall(scoped_refptr<IndexedDBCallbacks> callbacks,
72 IndexedDBConnection* connection,
73 int64 version)
74 : callbacks_(callbacks), connection_(connection), version_(version) {}
75 scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
76 IndexedDBConnection* connection() const { return connection_; }
77 int64 version() const { return version_; }
79 private:
80 scoped_refptr<IndexedDBCallbacks> callbacks_;
81 IndexedDBConnection* connection_;
82 int64 version_;
85 class IndexedDBDatabase::PendingDeleteCall {
86 public:
87 explicit PendingDeleteCall(scoped_refptr<IndexedDBCallbacks> callbacks)
88 : callbacks_(callbacks) {}
89 scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
91 private:
92 scoped_refptr<IndexedDBCallbacks> callbacks_;
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;
110 namespace {
111 const base::string16::value_type kNoStringVersion[] = {0};
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 DCHECK(factory != NULL);
129 void IndexedDBDatabase::AddObjectStore(
130 const IndexedDBObjectStoreMetadata& object_store,
131 int64 new_max_object_store_id) {
132 DCHECK(metadata_.object_stores.find(object_store.id) ==
133 metadata_.object_stores.end());
134 if (new_max_object_store_id != IndexedDBObjectStoreMetadata::kInvalidId) {
135 DCHECK_LT(metadata_.max_object_store_id, new_max_object_store_id);
136 metadata_.max_object_store_id = new_max_object_store_id;
138 metadata_.object_stores[object_store.id] = object_store;
141 void IndexedDBDatabase::RemoveObjectStore(int64 object_store_id) {
142 DCHECK(metadata_.object_stores.find(object_store_id) !=
143 metadata_.object_stores.end());
144 metadata_.object_stores.erase(object_store_id);
147 void IndexedDBDatabase::AddIndex(int64 object_store_id,
148 const IndexedDBIndexMetadata& index,
149 int64 new_max_index_id) {
150 DCHECK(metadata_.object_stores.find(object_store_id) !=
151 metadata_.object_stores.end());
152 IndexedDBObjectStoreMetadata object_store =
153 metadata_.object_stores[object_store_id];
155 DCHECK(object_store.indexes.find(index.id) == object_store.indexes.end());
156 object_store.indexes[index.id] = index;
157 if (new_max_index_id != IndexedDBIndexMetadata::kInvalidId) {
158 DCHECK_LT(object_store.max_index_id, new_max_index_id);
159 object_store.max_index_id = new_max_index_id;
161 metadata_.object_stores[object_store_id] = object_store;
164 void IndexedDBDatabase::RemoveIndex(int64 object_store_id, int64 index_id) {
165 DCHECK(metadata_.object_stores.find(object_store_id) !=
166 metadata_.object_stores.end());
167 IndexedDBObjectStoreMetadata object_store =
168 metadata_.object_stores[object_store_id];
170 DCHECK(object_store.indexes.find(index_id) != object_store.indexes.end());
171 object_store.indexes.erase(index_id);
172 metadata_.object_stores[object_store_id] = object_store;
175 leveldb::Status IndexedDBDatabase::OpenInternal() {
176 bool success = false;
177 leveldb::Status s = backing_store_->GetIDBDatabaseMetaData(
178 metadata_.name, &metadata_, &success);
179 DCHECK(success == (metadata_.id != kInvalidId)) << "success = " << success
180 << " id = " << metadata_.id;
181 if (!s.ok())
182 return s;
183 if (success)
184 return backing_store_->GetObjectStores(metadata_.id,
185 &metadata_.object_stores);
187 return backing_store_->CreateIDBDatabaseMetaData(
188 metadata_.name, metadata_.version, metadata_.int_version, &metadata_.id);
191 IndexedDBDatabase::~IndexedDBDatabase() {
192 DCHECK(transactions_.empty());
193 DCHECK(pending_open_calls_.empty());
194 DCHECK(pending_delete_calls_.empty());
197 scoped_ptr<IndexedDBConnection> IndexedDBDatabase::CreateConnection(
198 scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
199 int child_process_id) {
200 scoped_ptr<IndexedDBConnection> connection(
201 new IndexedDBConnection(this, database_callbacks));
202 connections_.insert(connection.get());
203 backing_store_->GrantChildProcessPermissions(child_process_id);
204 return connection.Pass();
207 IndexedDBTransaction* IndexedDBDatabase::GetTransaction(
208 int64 transaction_id) const {
209 TransactionMap::const_iterator trans_iterator =
210 transactions_.find(transaction_id);
211 if (trans_iterator == transactions_.end())
212 return NULL;
213 return trans_iterator->second;
216 bool IndexedDBDatabase::ValidateObjectStoreId(int64 object_store_id) const {
217 if (!ContainsKey(metadata_.object_stores, object_store_id)) {
218 DLOG(ERROR) << "Invalid object_store_id";
219 return false;
221 return true;
224 bool IndexedDBDatabase::ValidateObjectStoreIdAndIndexId(int64 object_store_id,
225 int64 index_id) const {
226 if (!ValidateObjectStoreId(object_store_id))
227 return false;
228 const IndexedDBObjectStoreMetadata& object_store_metadata =
229 metadata_.object_stores.find(object_store_id)->second;
230 if (!ContainsKey(object_store_metadata.indexes, index_id)) {
231 DLOG(ERROR) << "Invalid index_id";
232 return false;
234 return true;
237 bool IndexedDBDatabase::ValidateObjectStoreIdAndOptionalIndexId(
238 int64 object_store_id,
239 int64 index_id) const {
240 if (!ValidateObjectStoreId(object_store_id))
241 return false;
242 const IndexedDBObjectStoreMetadata& object_store_metadata =
243 metadata_.object_stores.find(object_store_id)->second;
244 if (index_id != IndexedDBIndexMetadata::kInvalidId &&
245 !ContainsKey(object_store_metadata.indexes, index_id)) {
246 DLOG(ERROR) << "Invalid index_id";
247 return false;
249 return true;
252 bool IndexedDBDatabase::ValidateObjectStoreIdAndNewIndexId(
253 int64 object_store_id,
254 int64 index_id) const {
255 if (!ValidateObjectStoreId(object_store_id))
256 return false;
257 const IndexedDBObjectStoreMetadata& object_store_metadata =
258 metadata_.object_stores.find(object_store_id)->second;
259 if (ContainsKey(object_store_metadata.indexes, index_id)) {
260 DLOG(ERROR) << "Invalid index_id";
261 return false;
263 return true;
266 void IndexedDBDatabase::CreateObjectStore(int64 transaction_id,
267 int64 object_store_id,
268 const base::string16& name,
269 const IndexedDBKeyPath& key_path,
270 bool auto_increment) {
271 IDB_TRACE1("IndexedDBDatabase::CreateObjectStore", "txn.id", transaction_id);
272 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
273 if (!transaction)
274 return;
275 DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
277 if (ContainsKey(metadata_.object_stores, object_store_id)) {
278 DLOG(ERROR) << "Invalid object_store_id";
279 return;
282 // Store creation is done synchronously, as it may be followed by
283 // index creation (also sync) since preemptive OpenCursor/SetIndexKeys
284 // may follow.
285 IndexedDBObjectStoreMetadata object_store_metadata(
286 name,
287 object_store_id,
288 key_path,
289 auto_increment,
290 IndexedDBDatabase::kMinimumIndexId);
292 leveldb::Status s =
293 backing_store_->CreateObjectStore(transaction->BackingStoreTransaction(),
294 transaction->database()->id(),
295 object_store_metadata.id,
296 object_store_metadata.name,
297 object_store_metadata.key_path,
298 object_store_metadata.auto_increment);
299 if (!s.ok()) {
300 IndexedDBDatabaseError error(
301 blink::WebIDBDatabaseExceptionUnknownError,
302 ASCIIToUTF16("Internal error creating object store '") +
303 object_store_metadata.name + ASCIIToUTF16("'."));
304 transaction->Abort(error);
305 if (s.IsCorruption())
306 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
307 error);
308 return;
311 AddObjectStore(object_store_metadata, object_store_id);
312 transaction->ScheduleAbortTask(
313 base::Bind(&IndexedDBDatabase::CreateObjectStoreAbortOperation,
314 this,
315 object_store_id));
318 void IndexedDBDatabase::DeleteObjectStore(int64 transaction_id,
319 int64 object_store_id) {
320 IDB_TRACE1("IndexedDBDatabase::DeleteObjectStore", "txn.id", transaction_id);
321 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
322 if (!transaction)
323 return;
324 DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
326 if (!ValidateObjectStoreId(object_store_id))
327 return;
329 transaction->ScheduleTask(
330 base::Bind(&IndexedDBDatabase::DeleteObjectStoreOperation,
331 this,
332 object_store_id));
335 void IndexedDBDatabase::CreateIndex(int64 transaction_id,
336 int64 object_store_id,
337 int64 index_id,
338 const base::string16& name,
339 const IndexedDBKeyPath& key_path,
340 bool unique,
341 bool multi_entry) {
342 IDB_TRACE1("IndexedDBDatabase::CreateIndex", "txn.id", transaction_id);
343 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
344 if (!transaction)
345 return;
346 DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
348 if (!ValidateObjectStoreIdAndNewIndexId(object_store_id, index_id))
349 return;
351 // Index creation is done synchronously since preemptive
352 // OpenCursor/SetIndexKeys may follow.
353 const IndexedDBIndexMetadata index_metadata(
354 name, index_id, key_path, unique, multi_entry);
356 if (!backing_store_->CreateIndex(transaction->BackingStoreTransaction(),
357 transaction->database()->id(),
358 object_store_id,
359 index_metadata.id,
360 index_metadata.name,
361 index_metadata.key_path,
362 index_metadata.unique,
363 index_metadata.multi_entry).ok()) {
364 base::string16 error_string =
365 ASCIIToUTF16("Internal error creating index '") +
366 index_metadata.name + ASCIIToUTF16("'.");
367 transaction->Abort(IndexedDBDatabaseError(
368 blink::WebIDBDatabaseExceptionUnknownError, error_string));
369 return;
372 AddIndex(object_store_id, index_metadata, index_id);
373 transaction->ScheduleAbortTask(
374 base::Bind(&IndexedDBDatabase::CreateIndexAbortOperation,
375 this,
376 object_store_id,
377 index_id));
380 void IndexedDBDatabase::CreateIndexAbortOperation(
381 int64 object_store_id,
382 int64 index_id,
383 IndexedDBTransaction* transaction) {
384 IDB_TRACE1("IndexedDBDatabase::CreateIndexAbortOperation",
385 "txn.id",
386 transaction->id());
387 DCHECK(!transaction);
388 RemoveIndex(object_store_id, index_id);
391 void IndexedDBDatabase::DeleteIndex(int64 transaction_id,
392 int64 object_store_id,
393 int64 index_id) {
394 IDB_TRACE1("IndexedDBDatabase::DeleteIndex", "txn.id", transaction_id);
395 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
396 if (!transaction)
397 return;
398 DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
400 if (!ValidateObjectStoreIdAndIndexId(object_store_id, index_id))
401 return;
403 transaction->ScheduleTask(
404 base::Bind(&IndexedDBDatabase::DeleteIndexOperation,
405 this,
406 object_store_id,
407 index_id));
410 void IndexedDBDatabase::DeleteIndexOperation(
411 int64 object_store_id,
412 int64 index_id,
413 IndexedDBTransaction* transaction) {
414 IDB_TRACE1(
415 "IndexedDBDatabase::DeleteIndexOperation", "txn.id", transaction->id());
417 const IndexedDBIndexMetadata index_metadata =
418 metadata_.object_stores[object_store_id].indexes[index_id];
420 leveldb::Status s =
421 backing_store_->DeleteIndex(transaction->BackingStoreTransaction(),
422 transaction->database()->id(),
423 object_store_id,
424 index_id);
425 if (!s.ok()) {
426 base::string16 error_string =
427 ASCIIToUTF16("Internal error deleting index '") +
428 index_metadata.name + ASCIIToUTF16("'.");
429 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
430 error_string);
431 transaction->Abort(error);
432 if (s.IsCorruption())
433 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
434 error);
435 return;
438 RemoveIndex(object_store_id, index_id);
439 transaction->ScheduleAbortTask(
440 base::Bind(&IndexedDBDatabase::DeleteIndexAbortOperation,
441 this,
442 object_store_id,
443 index_metadata));
446 void IndexedDBDatabase::DeleteIndexAbortOperation(
447 int64 object_store_id,
448 const IndexedDBIndexMetadata& index_metadata,
449 IndexedDBTransaction* transaction) {
450 DCHECK(!transaction);
451 IDB_TRACE1("IndexedDBDatabase::DeleteIndexAbortOperation",
452 "txn.id",
453 transaction->id());
454 AddIndex(object_store_id, index_metadata, IndexedDBIndexMetadata::kInvalidId);
457 void IndexedDBDatabase::Commit(int64 transaction_id) {
458 // The frontend suggests that we commit, but we may have previously initiated
459 // an abort, and so have disposed of the transaction. on_abort has already
460 // been dispatched to the frontend, so it will find out about that
461 // asynchronously.
462 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
463 if (transaction) {
464 scoped_refptr<IndexedDBFactory> factory = factory_;
465 leveldb::Status s = transaction->Commit();
466 if (s.IsCorruption()) {
467 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
468 "Internal error committing transaction.");
469 factory->HandleBackingStoreCorruption(identifier_.first, error);
474 void IndexedDBDatabase::Abort(int64 transaction_id) {
475 // If the transaction is unknown, then it has already been aborted by the
476 // backend before this call so it is safe to ignore it.
477 IDB_TRACE1("IndexedDBDatabase::Abort", "txn.id", transaction_id);
478 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
479 if (transaction)
480 transaction->Abort();
483 void IndexedDBDatabase::Abort(int64 transaction_id,
484 const IndexedDBDatabaseError& error) {
485 IDB_TRACE1("IndexedDBDatabase::Abort(error)", "txn.id", transaction_id);
486 // If the transaction is unknown, then it has already been aborted by the
487 // backend before this call so it is safe to ignore it.
488 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
489 if (transaction)
490 transaction->Abort(error);
493 void IndexedDBDatabase::Get(int64 transaction_id,
494 int64 object_store_id,
495 int64 index_id,
496 scoped_ptr<IndexedDBKeyRange> key_range,
497 bool key_only,
498 scoped_refptr<IndexedDBCallbacks> callbacks) {
499 IDB_TRACE1("IndexedDBDatabase::Get", "txn.id", transaction_id);
500 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
501 if (!transaction)
502 return;
504 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id))
505 return;
507 transaction->ScheduleTask(base::Bind(
508 &IndexedDBDatabase::GetOperation,
509 this,
510 object_store_id,
511 index_id,
512 Passed(&key_range),
513 key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE,
514 callbacks));
517 void IndexedDBDatabase::GetOperation(
518 int64 object_store_id,
519 int64 index_id,
520 scoped_ptr<IndexedDBKeyRange> key_range,
521 indexed_db::CursorType cursor_type,
522 scoped_refptr<IndexedDBCallbacks> callbacks,
523 IndexedDBTransaction* transaction) {
524 IDB_TRACE1("IndexedDBDatabase::GetOperation", "txn.id", transaction->id());
526 DCHECK(metadata_.object_stores.find(object_store_id) !=
527 metadata_.object_stores.end());
528 const IndexedDBObjectStoreMetadata& object_store_metadata =
529 metadata_.object_stores[object_store_id];
531 const IndexedDBKey* key;
533 leveldb::Status s;
534 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
535 if (key_range->IsOnlyKey()) {
536 key = &key_range->lower();
537 } else {
538 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
539 DCHECK_NE(cursor_type, indexed_db::CURSOR_KEY_ONLY);
540 // ObjectStore Retrieval Operation
541 backing_store_cursor = backing_store_->OpenObjectStoreCursor(
542 transaction->BackingStoreTransaction(),
543 id(),
544 object_store_id,
545 *key_range,
546 blink::WebIDBCursorDirectionNext,
547 &s);
548 } else if (cursor_type == indexed_db::CURSOR_KEY_ONLY) {
549 // Index Value Retrieval Operation
550 backing_store_cursor = backing_store_->OpenIndexKeyCursor(
551 transaction->BackingStoreTransaction(),
552 id(),
553 object_store_id,
554 index_id,
555 *key_range,
556 blink::WebIDBCursorDirectionNext,
557 &s);
558 } else {
559 // Index Referenced Value Retrieval Operation
560 backing_store_cursor = backing_store_->OpenIndexCursor(
561 transaction->BackingStoreTransaction(),
562 id(),
563 object_store_id,
564 index_id,
565 *key_range,
566 blink::WebIDBCursorDirectionNext,
567 &s);
570 if (!s.ok()) {
571 DLOG(ERROR) << "Unable to open cursor operation: " << s.ToString();
572 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
573 "Internal error deleting data in range");
574 if (s.IsCorruption()) {
575 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
576 error);
580 if (!backing_store_cursor) {
581 callbacks->OnSuccess();
582 return;
585 key = &backing_store_cursor->key();
588 scoped_ptr<IndexedDBKey> primary_key;
589 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
590 // Object Store Retrieval Operation
591 IndexedDBValue value;
592 s = backing_store_->GetRecord(transaction->BackingStoreTransaction(),
593 id(),
594 object_store_id,
595 *key,
596 &value);
597 if (!s.ok()) {
598 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
599 "Internal error in GetRecord.");
600 callbacks->OnError(error);
602 if (s.IsCorruption())
603 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
604 error);
605 return;
608 if (value.empty()) {
609 callbacks->OnSuccess();
610 return;
613 if (object_store_metadata.auto_increment &&
614 !object_store_metadata.key_path.IsNull()) {
615 callbacks->OnSuccess(&value, *key, object_store_metadata.key_path);
616 return;
619 callbacks->OnSuccess(&value);
620 return;
623 // From here we are dealing only with indexes.
624 s = backing_store_->GetPrimaryKeyViaIndex(
625 transaction->BackingStoreTransaction(),
626 id(),
627 object_store_id,
628 index_id,
629 *key,
630 &primary_key);
631 if (!s.ok()) {
632 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
633 "Internal error in GetPrimaryKeyViaIndex.");
634 callbacks->OnError(error);
635 if (s.IsCorruption())
636 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
637 error);
638 return;
640 if (!primary_key) {
641 callbacks->OnSuccess();
642 return;
644 if (cursor_type == indexed_db::CURSOR_KEY_ONLY) {
645 // Index Value Retrieval Operation
646 callbacks->OnSuccess(*primary_key);
647 return;
650 // Index Referenced Value Retrieval Operation
651 IndexedDBValue value;
652 s = backing_store_->GetRecord(transaction->BackingStoreTransaction(),
653 id(),
654 object_store_id,
655 *primary_key,
656 &value);
657 if (!s.ok()) {
658 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
659 "Internal error in GetRecord.");
660 callbacks->OnError(error);
661 if (s.IsCorruption())
662 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
663 error);
664 return;
667 if (value.empty()) {
668 callbacks->OnSuccess();
669 return;
671 if (object_store_metadata.auto_increment &&
672 !object_store_metadata.key_path.IsNull()) {
673 callbacks->OnSuccess(&value, *primary_key, object_store_metadata.key_path);
674 return;
676 callbacks->OnSuccess(&value);
679 static scoped_ptr<IndexedDBKey> GenerateKey(
680 IndexedDBBackingStore* backing_store,
681 IndexedDBTransaction* transaction,
682 int64 database_id,
683 int64 object_store_id) {
684 const int64 max_generator_value =
685 9007199254740992LL; // Maximum integer storable as ECMAScript number.
686 int64 current_number;
687 leveldb::Status s = backing_store->GetKeyGeneratorCurrentNumber(
688 transaction->BackingStoreTransaction(),
689 database_id,
690 object_store_id,
691 &current_number);
692 if (!s.ok()) {
693 LOG(ERROR) << "Failed to GetKeyGeneratorCurrentNumber";
694 return make_scoped_ptr(new IndexedDBKey());
696 if (current_number < 0 || current_number > max_generator_value)
697 return make_scoped_ptr(new IndexedDBKey());
699 return make_scoped_ptr(new IndexedDBKey(current_number, WebIDBKeyTypeNumber));
702 static leveldb::Status UpdateKeyGenerator(IndexedDBBackingStore* backing_store,
703 IndexedDBTransaction* transaction,
704 int64 database_id,
705 int64 object_store_id,
706 const IndexedDBKey& key,
707 bool check_current) {
708 DCHECK_EQ(WebIDBKeyTypeNumber, key.type());
709 return backing_store->MaybeUpdateKeyGeneratorCurrentNumber(
710 transaction->BackingStoreTransaction(),
711 database_id,
712 object_store_id,
713 static_cast<int64>(floor(key.number())) + 1,
714 check_current);
717 struct IndexedDBDatabase::PutOperationParams {
718 PutOperationParams() {}
719 int64 object_store_id;
720 IndexedDBValue value;
721 ScopedVector<storage::BlobDataHandle> handles;
722 scoped_ptr<IndexedDBKey> key;
723 blink::WebIDBPutMode put_mode;
724 scoped_refptr<IndexedDBCallbacks> callbacks;
725 std::vector<IndexKeys> index_keys;
727 private:
728 DISALLOW_COPY_AND_ASSIGN(PutOperationParams);
731 void IndexedDBDatabase::Put(int64 transaction_id,
732 int64 object_store_id,
733 IndexedDBValue* value,
734 ScopedVector<storage::BlobDataHandle>* handles,
735 scoped_ptr<IndexedDBKey> key,
736 blink::WebIDBPutMode put_mode,
737 scoped_refptr<IndexedDBCallbacks> callbacks,
738 const std::vector<IndexKeys>& index_keys) {
739 IDB_TRACE1("IndexedDBDatabase::Put", "txn.id", transaction_id);
740 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
741 if (!transaction)
742 return;
743 DCHECK_NE(transaction->mode(), blink::WebIDBTransactionModeReadOnly);
745 if (!ValidateObjectStoreId(object_store_id))
746 return;
748 DCHECK(key);
749 DCHECK(value);
750 scoped_ptr<PutOperationParams> params(new PutOperationParams());
751 params->object_store_id = object_store_id;
752 params->value.swap(*value);
753 params->handles.swap(*handles);
754 params->key = key.Pass();
755 params->put_mode = put_mode;
756 params->callbacks = callbacks;
757 params->index_keys = index_keys;
758 transaction->ScheduleTask(base::Bind(
759 &IndexedDBDatabase::PutOperation, this, base::Passed(&params)));
762 void IndexedDBDatabase::PutOperation(scoped_ptr<PutOperationParams> params,
763 IndexedDBTransaction* transaction) {
764 IDB_TRACE1("IndexedDBDatabase::PutOperation", "txn.id", transaction->id());
765 DCHECK_NE(transaction->mode(), blink::WebIDBTransactionModeReadOnly);
766 bool key_was_generated = false;
768 DCHECK(metadata_.object_stores.find(params->object_store_id) !=
769 metadata_.object_stores.end());
770 const IndexedDBObjectStoreMetadata& object_store =
771 metadata_.object_stores[params->object_store_id];
772 DCHECK(object_store.auto_increment || params->key->IsValid());
774 scoped_ptr<IndexedDBKey> key;
775 if (params->put_mode != blink::WebIDBPutModeCursorUpdate &&
776 object_store.auto_increment && !params->key->IsValid()) {
777 scoped_ptr<IndexedDBKey> auto_inc_key = GenerateKey(
778 backing_store_.get(), transaction, id(), params->object_store_id);
779 key_was_generated = true;
780 if (!auto_inc_key->IsValid()) {
781 params->callbacks->OnError(
782 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionConstraintError,
783 "Maximum key generator value reached."));
784 return;
786 key = auto_inc_key.Pass();
787 } else {
788 key = params->key.Pass();
791 DCHECK(key->IsValid());
793 IndexedDBBackingStore::RecordIdentifier record_identifier;
794 if (params->put_mode == blink::WebIDBPutModeAddOnly) {
795 bool found = false;
796 leveldb::Status s = backing_store_->KeyExistsInObjectStore(
797 transaction->BackingStoreTransaction(),
798 id(),
799 params->object_store_id,
800 *key,
801 &record_identifier,
802 &found);
803 if (!s.ok()) {
804 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
805 "Internal error checking key existence.");
806 params->callbacks->OnError(error);
807 if (s.IsCorruption())
808 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
809 error);
810 return;
812 if (found) {
813 params->callbacks->OnError(
814 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionConstraintError,
815 "Key already exists in the object store."));
816 return;
820 ScopedVector<IndexWriter> index_writers;
821 base::string16 error_message;
822 bool obeys_constraints = false;
823 bool backing_store_success = MakeIndexWriters(transaction,
824 backing_store_.get(),
825 id(),
826 object_store,
827 *key,
828 key_was_generated,
829 params->index_keys,
830 &index_writers,
831 &error_message,
832 &obeys_constraints);
833 if (!backing_store_success) {
834 params->callbacks->OnError(IndexedDBDatabaseError(
835 blink::WebIDBDatabaseExceptionUnknownError,
836 "Internal error: backing store error updating index keys."));
837 return;
839 if (!obeys_constraints) {
840 params->callbacks->OnError(IndexedDBDatabaseError(
841 blink::WebIDBDatabaseExceptionConstraintError, error_message));
842 return;
845 // Before this point, don't do any mutation. After this point, rollback the
846 // transaction in case of error.
847 leveldb::Status s =
848 backing_store_->PutRecord(transaction->BackingStoreTransaction(),
849 id(),
850 params->object_store_id,
851 *key,
852 &params->value,
853 &params->handles,
854 &record_identifier);
855 if (!s.ok()) {
856 IndexedDBDatabaseError error(
857 blink::WebIDBDatabaseExceptionUnknownError,
858 "Internal error: backing store error performing put/add.");
859 params->callbacks->OnError(error);
860 if (s.IsCorruption())
861 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
862 error);
863 return;
866 for (size_t i = 0; i < index_writers.size(); ++i) {
867 IndexWriter* index_writer = index_writers[i];
868 index_writer->WriteIndexKeys(record_identifier,
869 backing_store_.get(),
870 transaction->BackingStoreTransaction(),
871 id(),
872 params->object_store_id);
875 if (object_store.auto_increment &&
876 params->put_mode != blink::WebIDBPutModeCursorUpdate &&
877 key->type() == WebIDBKeyTypeNumber) {
878 leveldb::Status s = UpdateKeyGenerator(backing_store_.get(),
879 transaction,
880 id(),
881 params->object_store_id,
882 *key,
883 !key_was_generated);
884 if (!s.ok()) {
885 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
886 "Internal error updating key generator.");
887 params->callbacks->OnError(error);
888 if (s.IsCorruption())
889 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
890 error);
891 return;
895 params->callbacks->OnSuccess(*key);
898 void IndexedDBDatabase::SetIndexKeys(int64 transaction_id,
899 int64 object_store_id,
900 scoped_ptr<IndexedDBKey> primary_key,
901 const std::vector<IndexKeys>& index_keys) {
902 IDB_TRACE1("IndexedDBDatabase::SetIndexKeys", "txn.id", transaction_id);
903 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
904 if (!transaction)
905 return;
906 DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
908 // TODO(alecflett): This method could be asynchronous, but we need to
909 // evaluate if it's worth the extra complexity.
910 IndexedDBBackingStore::RecordIdentifier record_identifier;
911 bool found = false;
912 leveldb::Status s = backing_store_->KeyExistsInObjectStore(
913 transaction->BackingStoreTransaction(),
914 metadata_.id,
915 object_store_id,
916 *primary_key,
917 &record_identifier,
918 &found);
919 if (!s.ok()) {
920 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
921 "Internal error setting index keys.");
922 transaction->Abort(error);
923 if (s.IsCorruption())
924 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
925 error);
926 return;
928 if (!found) {
929 transaction->Abort(IndexedDBDatabaseError(
930 blink::WebIDBDatabaseExceptionUnknownError,
931 "Internal error setting index keys for object store."));
932 return;
935 ScopedVector<IndexWriter> index_writers;
936 base::string16 error_message;
937 bool obeys_constraints = false;
938 DCHECK(metadata_.object_stores.find(object_store_id) !=
939 metadata_.object_stores.end());
940 const IndexedDBObjectStoreMetadata& object_store_metadata =
941 metadata_.object_stores[object_store_id];
942 bool backing_store_success = MakeIndexWriters(transaction,
943 backing_store_.get(),
944 id(),
945 object_store_metadata,
946 *primary_key,
947 false,
948 index_keys,
949 &index_writers,
950 &error_message,
951 &obeys_constraints);
952 if (!backing_store_success) {
953 transaction->Abort(IndexedDBDatabaseError(
954 blink::WebIDBDatabaseExceptionUnknownError,
955 "Internal error: backing store error updating index keys."));
956 return;
958 if (!obeys_constraints) {
959 transaction->Abort(IndexedDBDatabaseError(
960 blink::WebIDBDatabaseExceptionConstraintError, error_message));
961 return;
964 for (size_t i = 0; i < index_writers.size(); ++i) {
965 IndexWriter* index_writer = index_writers[i];
966 index_writer->WriteIndexKeys(record_identifier,
967 backing_store_.get(),
968 transaction->BackingStoreTransaction(),
969 id(),
970 object_store_id);
974 void IndexedDBDatabase::SetIndexesReady(int64 transaction_id,
975 int64,
976 const std::vector<int64>& index_ids) {
977 IDB_TRACE1("IndexedDBDatabase::SetIndexesReady", "txn.id", transaction_id);
978 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
979 if (!transaction)
980 return;
981 DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
983 transaction->ScheduleTask(
984 blink::WebIDBTaskTypePreemptive,
985 base::Bind(&IndexedDBDatabase::SetIndexesReadyOperation,
986 this,
987 index_ids.size()));
990 void IndexedDBDatabase::SetIndexesReadyOperation(
991 size_t index_count,
992 IndexedDBTransaction* transaction) {
993 IDB_TRACE1("IndexedDBDatabase::SetIndexesReadyOperation",
994 "txn.id",
995 transaction->id());
996 for (size_t i = 0; i < index_count; ++i)
997 transaction->DidCompletePreemptiveEvent();
1000 struct IndexedDBDatabase::OpenCursorOperationParams {
1001 OpenCursorOperationParams() {}
1002 int64 object_store_id;
1003 int64 index_id;
1004 scoped_ptr<IndexedDBKeyRange> key_range;
1005 blink::WebIDBCursorDirection direction;
1006 indexed_db::CursorType cursor_type;
1007 blink::WebIDBTaskType task_type;
1008 scoped_refptr<IndexedDBCallbacks> callbacks;
1010 private:
1011 DISALLOW_COPY_AND_ASSIGN(OpenCursorOperationParams);
1014 void IndexedDBDatabase::OpenCursor(
1015 int64 transaction_id,
1016 int64 object_store_id,
1017 int64 index_id,
1018 scoped_ptr<IndexedDBKeyRange> key_range,
1019 blink::WebIDBCursorDirection direction,
1020 bool key_only,
1021 blink::WebIDBTaskType task_type,
1022 scoped_refptr<IndexedDBCallbacks> callbacks) {
1023 IDB_TRACE1("IndexedDBDatabase::OpenCursor", "txn.id", transaction_id);
1024 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1025 if (!transaction)
1026 return;
1028 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id))
1029 return;
1031 scoped_ptr<OpenCursorOperationParams> params(new OpenCursorOperationParams());
1032 params->object_store_id = object_store_id;
1033 params->index_id = index_id;
1034 params->key_range = key_range.Pass();
1035 params->direction = direction;
1036 params->cursor_type =
1037 key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE;
1038 params->task_type = task_type;
1039 params->callbacks = callbacks;
1040 transaction->ScheduleTask(base::Bind(
1041 &IndexedDBDatabase::OpenCursorOperation, this, base::Passed(&params)));
1044 void IndexedDBDatabase::OpenCursorOperation(
1045 scoped_ptr<OpenCursorOperationParams> params,
1046 IndexedDBTransaction* transaction) {
1047 IDB_TRACE1(
1048 "IndexedDBDatabase::OpenCursorOperation", "txn.id", transaction->id());
1050 // The frontend has begun indexing, so this pauses the transaction
1051 // until the indexing is complete. This can't happen any earlier
1052 // because we don't want to switch to early mode in case multiple
1053 // indexes are being created in a row, with Put()'s in between.
1054 if (params->task_type == blink::WebIDBTaskTypePreemptive)
1055 transaction->AddPreemptiveEvent();
1057 leveldb::Status s;
1058 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
1059 if (params->index_id == IndexedDBIndexMetadata::kInvalidId) {
1060 if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) {
1061 DCHECK_EQ(params->task_type, blink::WebIDBTaskTypeNormal);
1062 backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor(
1063 transaction->BackingStoreTransaction(),
1064 id(),
1065 params->object_store_id,
1066 *params->key_range,
1067 params->direction,
1068 &s);
1069 } else {
1070 backing_store_cursor = backing_store_->OpenObjectStoreCursor(
1071 transaction->BackingStoreTransaction(),
1072 id(),
1073 params->object_store_id,
1074 *params->key_range,
1075 params->direction,
1076 &s);
1078 } else {
1079 DCHECK_EQ(params->task_type, blink::WebIDBTaskTypeNormal);
1080 if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) {
1081 backing_store_cursor = backing_store_->OpenIndexKeyCursor(
1082 transaction->BackingStoreTransaction(),
1083 id(),
1084 params->object_store_id,
1085 params->index_id,
1086 *params->key_range,
1087 params->direction,
1088 &s);
1089 } else {
1090 backing_store_cursor = backing_store_->OpenIndexCursor(
1091 transaction->BackingStoreTransaction(),
1092 id(),
1093 params->object_store_id,
1094 params->index_id,
1095 *params->key_range,
1096 params->direction,
1097 &s);
1101 if (!s.ok()) {
1102 DLOG(ERROR) << "Unable to open cursor operation: " << s.ToString();
1103 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1104 "Internal error opening cursor operation");
1105 if (s.IsCorruption()) {
1106 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1107 error);
1111 if (!backing_store_cursor) {
1112 // Why is Success being called?
1113 params->callbacks->OnSuccess(static_cast<IndexedDBValue*>(NULL));
1114 return;
1117 scoped_refptr<IndexedDBCursor> cursor =
1118 new IndexedDBCursor(backing_store_cursor.Pass(),
1119 params->cursor_type,
1120 params->task_type,
1121 transaction);
1122 params->callbacks->OnSuccess(
1123 cursor, cursor->key(), cursor->primary_key(), cursor->Value());
1126 void IndexedDBDatabase::Count(int64 transaction_id,
1127 int64 object_store_id,
1128 int64 index_id,
1129 scoped_ptr<IndexedDBKeyRange> key_range,
1130 scoped_refptr<IndexedDBCallbacks> callbacks) {
1131 IDB_TRACE1("IndexedDBDatabase::Count", "txn.id", transaction_id);
1132 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1133 if (!transaction)
1134 return;
1136 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id))
1137 return;
1139 transaction->ScheduleTask(base::Bind(&IndexedDBDatabase::CountOperation,
1140 this,
1141 object_store_id,
1142 index_id,
1143 base::Passed(&key_range),
1144 callbacks));
1147 void IndexedDBDatabase::CountOperation(
1148 int64 object_store_id,
1149 int64 index_id,
1150 scoped_ptr<IndexedDBKeyRange> key_range,
1151 scoped_refptr<IndexedDBCallbacks> callbacks,
1152 IndexedDBTransaction* transaction) {
1153 IDB_TRACE1("IndexedDBDatabase::CountOperation", "txn.id", transaction->id());
1154 uint32 count = 0;
1155 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
1157 leveldb::Status s;
1158 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
1159 backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor(
1160 transaction->BackingStoreTransaction(),
1161 id(),
1162 object_store_id,
1163 *key_range,
1164 blink::WebIDBCursorDirectionNext,
1165 &s);
1166 } else {
1167 backing_store_cursor = backing_store_->OpenIndexKeyCursor(
1168 transaction->BackingStoreTransaction(),
1169 id(),
1170 object_store_id,
1171 index_id,
1172 *key_range,
1173 blink::WebIDBCursorDirectionNext,
1174 &s);
1176 if (!s.ok()) {
1177 DLOG(ERROR) << "Unable perform count operation: " << s.ToString();
1178 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1179 "Internal error performing count operation");
1180 if (s.IsCorruption()) {
1181 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1182 error);
1185 if (!backing_store_cursor) {
1186 callbacks->OnSuccess(count);
1187 return;
1190 do {
1191 ++count;
1192 } while (backing_store_cursor->Continue(&s));
1194 // TODO(cmumford): Check for database corruption.
1196 callbacks->OnSuccess(count);
1199 void IndexedDBDatabase::DeleteRange(
1200 int64 transaction_id,
1201 int64 object_store_id,
1202 scoped_ptr<IndexedDBKeyRange> key_range,
1203 scoped_refptr<IndexedDBCallbacks> callbacks) {
1204 IDB_TRACE1("IndexedDBDatabase::DeleteRange", "txn.id", transaction_id);
1205 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1206 if (!transaction)
1207 return;
1208 DCHECK_NE(transaction->mode(), blink::WebIDBTransactionModeReadOnly);
1210 if (!ValidateObjectStoreId(object_store_id))
1211 return;
1213 transaction->ScheduleTask(base::Bind(&IndexedDBDatabase::DeleteRangeOperation,
1214 this,
1215 object_store_id,
1216 base::Passed(&key_range),
1217 callbacks));
1220 void IndexedDBDatabase::DeleteRangeOperation(
1221 int64 object_store_id,
1222 scoped_ptr<IndexedDBKeyRange> key_range,
1223 scoped_refptr<IndexedDBCallbacks> callbacks,
1224 IndexedDBTransaction* transaction) {
1225 IDB_TRACE1(
1226 "IndexedDBDatabase::DeleteRangeOperation", "txn.id", transaction->id());
1227 leveldb::Status s =
1228 backing_store_->DeleteRange(transaction->BackingStoreTransaction(),
1229 id(),
1230 object_store_id,
1231 *key_range);
1232 if (!s.ok()) {
1233 base::string16 error_string =
1234 ASCIIToUTF16("Internal error deleting data in range");
1235 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1236 error_string);
1237 transaction->Abort(error);
1238 if (s.IsCorruption()) {
1239 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1240 error);
1242 return;
1244 callbacks->OnSuccess();
1247 void IndexedDBDatabase::Clear(int64 transaction_id,
1248 int64 object_store_id,
1249 scoped_refptr<IndexedDBCallbacks> callbacks) {
1250 IDB_TRACE1("IndexedDBDatabase::Clear", "txn.id", transaction_id);
1251 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1252 if (!transaction)
1253 return;
1254 DCHECK_NE(transaction->mode(), blink::WebIDBTransactionModeReadOnly);
1256 if (!ValidateObjectStoreId(object_store_id))
1257 return;
1259 transaction->ScheduleTask(base::Bind(
1260 &IndexedDBDatabase::ClearOperation, this, object_store_id, callbacks));
1263 void IndexedDBDatabase::ClearOperation(
1264 int64 object_store_id,
1265 scoped_refptr<IndexedDBCallbacks> callbacks,
1266 IndexedDBTransaction* transaction) {
1267 IDB_TRACE1("IndexedDBDatabase::ClearOperation", "txn.id", transaction->id());
1268 leveldb::Status s = backing_store_->ClearObjectStore(
1269 transaction->BackingStoreTransaction(), id(), object_store_id);
1270 if (!s.ok()) {
1271 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1272 "Internal error clearing object store");
1273 callbacks->OnError(error);
1274 if (s.IsCorruption()) {
1275 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1276 error);
1278 return;
1280 callbacks->OnSuccess();
1283 void IndexedDBDatabase::DeleteObjectStoreOperation(
1284 int64 object_store_id,
1285 IndexedDBTransaction* transaction) {
1286 IDB_TRACE1("IndexedDBDatabase::DeleteObjectStoreOperation",
1287 "txn.id",
1288 transaction->id());
1290 const IndexedDBObjectStoreMetadata object_store_metadata =
1291 metadata_.object_stores[object_store_id];
1292 leveldb::Status s =
1293 backing_store_->DeleteObjectStore(transaction->BackingStoreTransaction(),
1294 transaction->database()->id(),
1295 object_store_id);
1296 if (!s.ok()) {
1297 base::string16 error_string =
1298 ASCIIToUTF16("Internal error deleting object store '") +
1299 object_store_metadata.name + ASCIIToUTF16("'.");
1300 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1301 error_string);
1302 transaction->Abort(error);
1303 if (s.IsCorruption())
1304 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1305 error);
1306 return;
1309 RemoveObjectStore(object_store_id);
1310 transaction->ScheduleAbortTask(
1311 base::Bind(&IndexedDBDatabase::DeleteObjectStoreAbortOperation,
1312 this,
1313 object_store_metadata));
1316 void IndexedDBDatabase::VersionChangeOperation(
1317 int64 version,
1318 scoped_refptr<IndexedDBCallbacks> callbacks,
1319 scoped_ptr<IndexedDBConnection> connection,
1320 IndexedDBTransaction* transaction) {
1321 IDB_TRACE1(
1322 "IndexedDBDatabase::VersionChangeOperation", "txn.id", transaction->id());
1323 int64 old_version = metadata_.int_version;
1324 DCHECK_GT(version, old_version);
1326 if (!backing_store_->UpdateIDBDatabaseIntVersion(
1327 transaction->BackingStoreTransaction(), id(), version)) {
1328 IndexedDBDatabaseError error(
1329 blink::WebIDBDatabaseExceptionUnknownError,
1330 ASCIIToUTF16(
1331 "Internal error writing data to stable storage when "
1332 "updating version."));
1333 callbacks->OnError(error);
1334 transaction->Abort(error);
1335 return;
1338 transaction->ScheduleAbortTask(
1339 base::Bind(&IndexedDBDatabase::VersionChangeAbortOperation,
1340 this,
1341 metadata_.version,
1342 metadata_.int_version));
1343 metadata_.int_version = version;
1344 metadata_.version = kNoStringVersion;
1346 DCHECK(!pending_second_half_open_);
1347 pending_second_half_open_.reset(
1348 new PendingSuccessCall(callbacks, connection.get(), version));
1349 callbacks->OnUpgradeNeeded(old_version, connection.Pass(), metadata());
1352 void IndexedDBDatabase::TransactionFinished(IndexedDBTransaction* transaction,
1353 bool committed) {
1354 DCHECK(transactions_.find(transaction->id()) != transactions_.end());
1355 DCHECK_EQ(transactions_[transaction->id()], transaction);
1356 transactions_.erase(transaction->id());
1358 if (transaction->mode() == blink::WebIDBTransactionModeVersionChange) {
1359 if (pending_second_half_open_) {
1360 if (committed) {
1361 DCHECK_EQ(pending_second_half_open_->version(), metadata_.int_version);
1362 DCHECK(metadata_.id != kInvalidId);
1364 // Connection was already minted for OnUpgradeNeeded callback.
1365 scoped_ptr<IndexedDBConnection> connection;
1366 pending_second_half_open_->callbacks()->OnSuccess(connection.Pass(),
1367 this->metadata());
1368 } else {
1369 pending_second_half_open_->callbacks()->OnError(
1370 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError,
1371 "Version change transaction was aborted in "
1372 "upgradeneeded event handler."));
1374 pending_second_half_open_.reset();
1377 // Connection queue is now unblocked.
1378 ProcessPendingCalls();
1382 void IndexedDBDatabase::TransactionCommitFailed(const leveldb::Status& status) {
1383 if (status.IsCorruption()) {
1384 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1385 "Error committing transaction");
1386 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), error);
1387 } else {
1388 factory_->HandleBackingStoreFailure(backing_store_->origin_url());
1392 size_t IndexedDBDatabase::ConnectionCount() const {
1393 // This does not include pending open calls, as those should not block version
1394 // changes and deletes.
1395 return connections_.size();
1398 size_t IndexedDBDatabase::PendingOpenCount() const {
1399 return pending_open_calls_.size();
1402 size_t IndexedDBDatabase::PendingUpgradeCount() const {
1403 return pending_run_version_change_transaction_call_ ? 1 : 0;
1406 size_t IndexedDBDatabase::RunningUpgradeCount() const {
1407 return pending_second_half_open_ ? 1 : 0;
1410 size_t IndexedDBDatabase::PendingDeleteCount() const {
1411 return pending_delete_calls_.size();
1414 void IndexedDBDatabase::ProcessPendingCalls() {
1415 if (pending_run_version_change_transaction_call_ && ConnectionCount() == 1) {
1416 DCHECK(pending_run_version_change_transaction_call_->version() >
1417 metadata_.int_version);
1418 scoped_ptr<PendingUpgradeCall> pending_call =
1419 pending_run_version_change_transaction_call_.Pass();
1420 RunVersionChangeTransactionFinal(pending_call->callbacks(),
1421 pending_call->ReleaseConnection(),
1422 pending_call->transaction_id(),
1423 pending_call->version());
1424 DCHECK_EQ(1u, ConnectionCount());
1425 // Fall through would be a no-op, since transaction must complete
1426 // asynchronously.
1427 DCHECK(IsDeleteDatabaseBlocked());
1428 DCHECK(IsOpenConnectionBlocked());
1429 return;
1432 if (!IsDeleteDatabaseBlocked()) {
1433 PendingDeleteCallList pending_delete_calls;
1434 pending_delete_calls_.swap(pending_delete_calls);
1435 while (!pending_delete_calls.empty()) {
1436 // Only the first delete call will delete the database, but each must fire
1437 // callbacks.
1438 scoped_ptr<PendingDeleteCall> pending_delete_call(
1439 pending_delete_calls.front());
1440 pending_delete_calls.pop_front();
1441 DeleteDatabaseFinal(pending_delete_call->callbacks());
1443 // delete_database_final should never re-queue calls.
1444 DCHECK(pending_delete_calls_.empty());
1445 // Fall through when complete, as pending opens may be unblocked.
1448 if (!IsOpenConnectionBlocked()) {
1449 PendingOpenCallList pending_open_calls;
1450 pending_open_calls_.swap(pending_open_calls);
1451 while (!pending_open_calls.empty()) {
1452 OpenConnection(pending_open_calls.front());
1453 pending_open_calls.pop_front();
1458 void IndexedDBDatabase::CreateTransaction(
1459 int64 transaction_id,
1460 IndexedDBConnection* connection,
1461 const std::vector<int64>& object_store_ids,
1462 blink::WebIDBTransactionMode mode) {
1463 IDB_TRACE1("IndexedDBDatabase::CreateTransaction", "txn.id", transaction_id);
1464 DCHECK(connections_.count(connection));
1465 DCHECK(transactions_.find(transaction_id) == transactions_.end());
1466 if (transactions_.find(transaction_id) != transactions_.end())
1467 return;
1469 // The transaction will add itself to this database's coordinator, which
1470 // manages the lifetime of the object.
1471 TransactionCreated(new IndexedDBTransaction(
1472 transaction_id,
1473 connection->callbacks(),
1474 std::set<int64>(object_store_ids.begin(), object_store_ids.end()),
1475 mode,
1476 this,
1477 new IndexedDBBackingStore::Transaction(backing_store_.get())));
1480 void IndexedDBDatabase::TransactionCreated(IndexedDBTransaction* transaction) {
1481 transactions_[transaction->id()] = transaction;
1484 bool IndexedDBDatabase::IsOpenConnectionBlocked() const {
1485 return !pending_delete_calls_.empty() ||
1486 transaction_coordinator_.IsRunningVersionChangeTransaction() ||
1487 pending_run_version_change_transaction_call_;
1490 void IndexedDBDatabase::OpenConnection(
1491 const IndexedDBPendingConnection& connection) {
1492 DCHECK(backing_store_.get());
1494 // TODO(jsbell): Should have a priority queue so that higher version
1495 // requests are processed first. http://crbug.com/225850
1496 if (IsOpenConnectionBlocked()) {
1497 // The backing store only detects data loss when it is first opened. The
1498 // presence of existing connections means we didn't even check for data loss
1499 // so there'd better not be any.
1500 DCHECK_NE(blink::WebIDBDataLossTotal, connection.callbacks->data_loss());
1501 pending_open_calls_.push_back(connection);
1502 return;
1505 if (metadata_.id == kInvalidId) {
1506 // The database was deleted then immediately re-opened; OpenInternal()
1507 // recreates it in the backing store.
1508 if (OpenInternal().ok()) {
1509 DCHECK_EQ(IndexedDBDatabaseMetadata::NO_INT_VERSION,
1510 metadata_.int_version);
1511 } else {
1512 base::string16 message;
1513 if (connection.version == IndexedDBDatabaseMetadata::NO_INT_VERSION) {
1514 message = ASCIIToUTF16(
1515 "Internal error opening database with no version specified.");
1516 } else {
1517 message =
1518 ASCIIToUTF16("Internal error opening database with version ") +
1519 Int64ToString16(connection.version);
1521 connection.callbacks->OnError(IndexedDBDatabaseError(
1522 blink::WebIDBDatabaseExceptionUnknownError, message));
1523 return;
1527 // We infer that the database didn't exist from its lack of either type of
1528 // version.
1529 bool is_new_database =
1530 metadata_.version == kNoStringVersion &&
1531 metadata_.int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION;
1533 if (connection.version == IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION) {
1534 // For unit tests only - skip upgrade steps. Calling from script with
1535 // DEFAULT_INT_VERSION throws exception.
1536 // TODO(jsbell): DCHECK that not in unit tests.
1537 DCHECK(is_new_database);
1538 connection.callbacks->OnSuccess(
1539 CreateConnection(connection.database_callbacks,
1540 connection.child_process_id),
1541 this->metadata());
1542 return;
1545 // We may need to change the version.
1546 int64 local_version = connection.version;
1547 if (local_version == IndexedDBDatabaseMetadata::NO_INT_VERSION) {
1548 if (!is_new_database) {
1549 connection.callbacks->OnSuccess(
1550 CreateConnection(connection.database_callbacks,
1551 connection.child_process_id),
1552 this->metadata());
1553 return;
1555 // Spec says: If no version is specified and no database exists, set
1556 // database version to 1.
1557 local_version = 1;
1560 if (local_version > metadata_.int_version) {
1561 RunVersionChangeTransaction(connection.callbacks,
1562 CreateConnection(connection.database_callbacks,
1563 connection.child_process_id),
1564 connection.transaction_id,
1565 local_version);
1566 return;
1568 if (local_version < metadata_.int_version) {
1569 connection.callbacks->OnError(IndexedDBDatabaseError(
1570 blink::WebIDBDatabaseExceptionVersionError,
1571 ASCIIToUTF16("The requested version (") +
1572 Int64ToString16(local_version) +
1573 ASCIIToUTF16(") is less than the existing version (") +
1574 Int64ToString16(metadata_.int_version) + ASCIIToUTF16(").")));
1575 return;
1577 DCHECK_EQ(local_version, metadata_.int_version);
1578 connection.callbacks->OnSuccess(
1579 CreateConnection(connection.database_callbacks,
1580 connection.child_process_id),
1581 this->metadata());
1584 void IndexedDBDatabase::RunVersionChangeTransaction(
1585 scoped_refptr<IndexedDBCallbacks> callbacks,
1586 scoped_ptr<IndexedDBConnection> connection,
1587 int64 transaction_id,
1588 int64 requested_version) {
1589 DCHECK(callbacks.get());
1590 DCHECK(connections_.count(connection.get()));
1591 if (ConnectionCount() > 1) {
1592 DCHECK_NE(blink::WebIDBDataLossTotal, callbacks->data_loss());
1593 // Front end ensures the event is not fired at connections that have
1594 // close_pending set.
1595 for (const auto* iter : connections_) {
1596 if (iter != connection.get()) {
1597 iter->callbacks()->OnVersionChange(metadata_.int_version,
1598 requested_version);
1601 // OnBlocked will be fired at the request when one of the other
1602 // connections acks that the OnVersionChange was ignored.
1604 DCHECK(!pending_run_version_change_transaction_call_);
1605 pending_run_version_change_transaction_call_.reset(new PendingUpgradeCall(
1606 callbacks, connection.Pass(), transaction_id, requested_version));
1607 return;
1609 RunVersionChangeTransactionFinal(
1610 callbacks, connection.Pass(), transaction_id, requested_version);
1613 void IndexedDBDatabase::RunVersionChangeTransactionFinal(
1614 scoped_refptr<IndexedDBCallbacks> callbacks,
1615 scoped_ptr<IndexedDBConnection> connection,
1616 int64 transaction_id,
1617 int64 requested_version) {
1619 std::vector<int64> object_store_ids;
1620 CreateTransaction(transaction_id,
1621 connection.get(),
1622 object_store_ids,
1623 blink::WebIDBTransactionModeVersionChange);
1625 transactions_[transaction_id]->ScheduleTask(
1626 base::Bind(&IndexedDBDatabase::VersionChangeOperation,
1627 this,
1628 requested_version,
1629 callbacks,
1630 base::Passed(&connection)));
1631 DCHECK(!pending_second_half_open_);
1634 void IndexedDBDatabase::DeleteDatabase(
1635 scoped_refptr<IndexedDBCallbacks> callbacks) {
1637 if (IsDeleteDatabaseBlocked()) {
1638 for (const auto* connection : connections_) {
1639 // Front end ensures the event is not fired at connections that have
1640 // close_pending set.
1641 connection->callbacks()->OnVersionChange(
1642 metadata_.int_version, IndexedDBDatabaseMetadata::NO_INT_VERSION);
1644 // OnBlocked will be fired at the request when one of the other
1645 // connections acks that the OnVersionChange was ignored.
1647 pending_delete_calls_.push_back(new PendingDeleteCall(callbacks));
1648 return;
1650 DeleteDatabaseFinal(callbacks);
1653 bool IndexedDBDatabase::IsDeleteDatabaseBlocked() const {
1654 return !!ConnectionCount();
1657 void IndexedDBDatabase::DeleteDatabaseFinal(
1658 scoped_refptr<IndexedDBCallbacks> callbacks) {
1659 DCHECK(!IsDeleteDatabaseBlocked());
1660 DCHECK(backing_store_.get());
1661 leveldb::Status s = backing_store_->DeleteDatabase(metadata_.name);
1662 if (!s.ok()) {
1663 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1664 "Internal error deleting database.");
1665 callbacks->OnError(error);
1666 if (s.IsCorruption()) {
1667 GURL origin_url = backing_store_->origin_url();
1668 backing_store_ = NULL;
1669 factory_->HandleBackingStoreCorruption(origin_url, error);
1671 return;
1673 int64 old_version = metadata_.int_version;
1674 metadata_.version = kNoStringVersion;
1675 metadata_.id = kInvalidId;
1676 metadata_.int_version = IndexedDBDatabaseMetadata::NO_INT_VERSION;
1677 metadata_.object_stores.clear();
1678 callbacks->OnSuccess(old_version);
1679 factory_->DatabaseDeleted(identifier_);
1682 void IndexedDBDatabase::ForceClose() {
1683 // IndexedDBConnection::ForceClose() may delete this database, so hold ref.
1684 scoped_refptr<IndexedDBDatabase> protect(this);
1685 ConnectionSet::const_iterator it = connections_.begin();
1686 while (it != connections_.end()) {
1687 IndexedDBConnection* connection = *it++;
1688 connection->ForceClose();
1690 DCHECK(connections_.empty());
1693 void IndexedDBDatabase::VersionChangeIgnored() {
1694 if (pending_run_version_change_transaction_call_)
1695 pending_run_version_change_transaction_call_->callbacks()->OnBlocked(
1696 metadata_.int_version);
1698 for (const auto& pending_delete_call : pending_delete_calls_)
1699 pending_delete_call->callbacks()->OnBlocked(metadata_.int_version);
1703 void IndexedDBDatabase::Close(IndexedDBConnection* connection, bool forced) {
1704 DCHECK(connections_.count(connection));
1705 DCHECK(connection->IsConnected());
1706 DCHECK(connection->database() == this);
1708 IDB_TRACE("IndexedDBDatabase::Close");
1710 connections_.erase(connection);
1712 // Abort outstanding transactions from the closing connection. This
1713 // can not happen if the close is requested by the connection itself
1714 // as the front-end defers the close until all transactions are
1715 // complete, but can occur on process termination or forced close.
1717 TransactionMap transactions(transactions_);
1718 for (const auto& it : transactions) {
1719 if (it.second->connection() == connection->callbacks())
1720 it.second->Abort(
1721 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
1722 "Connection is closing."));
1726 if (pending_second_half_open_ &&
1727 pending_second_half_open_->connection() == connection) {
1728 pending_second_half_open_->callbacks()->OnError(
1729 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError,
1730 "The connection was closed."));
1731 pending_second_half_open_.reset();
1734 ProcessPendingCalls();
1736 // TODO(jsbell): Add a test for the pending_open_calls_ cases below.
1737 if (!ConnectionCount() && !pending_open_calls_.size() &&
1738 !pending_delete_calls_.size()) {
1739 DCHECK(transactions_.empty());
1741 const GURL origin_url = backing_store_->origin_url();
1742 backing_store_ = NULL;
1744 factory_->ReleaseDatabase(identifier_, forced);
1748 void IndexedDBDatabase::CreateObjectStoreAbortOperation(
1749 int64 object_store_id,
1750 IndexedDBTransaction* transaction) {
1751 DCHECK(!transaction);
1752 IDB_TRACE1("IndexedDBDatabase::CreateObjectStoreAbortOperation",
1753 "txn.id",
1754 transaction->id());
1755 RemoveObjectStore(object_store_id);
1758 void IndexedDBDatabase::DeleteObjectStoreAbortOperation(
1759 const IndexedDBObjectStoreMetadata& object_store_metadata,
1760 IndexedDBTransaction* transaction) {
1761 DCHECK(!transaction);
1762 IDB_TRACE1("IndexedDBDatabase::DeleteObjectStoreAbortOperation",
1763 "txn.id",
1764 transaction->id());
1765 AddObjectStore(object_store_metadata,
1766 IndexedDBObjectStoreMetadata::kInvalidId);
1769 void IndexedDBDatabase::VersionChangeAbortOperation(
1770 const base::string16& previous_version,
1771 int64 previous_int_version,
1772 IndexedDBTransaction* transaction) {
1773 DCHECK(!transaction);
1774 IDB_TRACE1("IndexedDBDatabase::VersionChangeAbortOperation",
1775 "txn.id",
1776 transaction->id());
1777 metadata_.version = previous_version;
1778 metadata_.int_version = previous_int_version;
1781 } // namespace content