Index: content/browser/indexed_db/indexed_db_database.cc |
diff --git a/content/browser/indexed_db/indexed_db_database.cc b/content/browser/indexed_db/indexed_db_database.cc |
index de9895b10470a0c32c2cfffbffd8d08983f2dd10..0835c8aa2cdc35f42841e6829e23109faadd8789 100644 |
--- a/content/browser/indexed_db/indexed_db_database.cc |
+++ b/content/browser/indexed_db/indexed_db_database.cc |
@@ -233,6 +233,7 @@ IndexedDBDatabase::~IndexedDBDatabase() { |
DCHECK(transactions_.empty()); |
DCHECK(pending_open_calls_.empty()); |
DCHECK(pending_delete_calls_.empty()); |
+ DCHECK(blocked_delete_calls_.empty()); |
} |
size_t IndexedDBDatabase::GetMaxMessageSizeInBytes() const { |
@@ -1636,31 +1637,58 @@ void IndexedDBDatabase::ProcessPendingCalls() { |
DCHECK_EQ(1u, ConnectionCount()); |
// Fall through would be a no-op, since transaction must complete |
// asynchronously. |
+ DCHECK(IsUpgradeRunning()); |
DCHECK(IsDeleteDatabaseBlocked()); |
DCHECK(IsOpenConnectionBlocked()); |
return; |
} |
- if (!IsDeleteDatabaseBlocked()) { |
- std::list<PendingDeleteCall*> pending_delete_calls; |
+ if (IsUpgradeRunning()) { |
+ DCHECK_EQ(ConnectionCount(), 1UL); |
+ return; |
+ } |
+ |
+ // These delete calls were blocked by a running upgrade, and did not even |
+ // get as far as broadcasting OnVersionChange (if needed). |
+ { |
+ std::list<std::unique_ptr<PendingDeleteCall>> pending_delete_calls; |
pending_delete_calls_.swap(pending_delete_calls); |
while (!pending_delete_calls.empty()) { |
+ std::unique_ptr<PendingDeleteCall> pending_delete_call( |
+ std::move(pending_delete_calls.front())); |
+ pending_delete_calls.pop_front(); |
+ DeleteDatabase(pending_delete_call->callbacks()); |
+ } |
+ // DeleteDatabase should never re-queue these calls. |
+ DCHECK(pending_delete_calls_.empty()); |
+ // Fall through when complete, as pending deletes/opens may be unblocked. |
+ } |
+ |
+ // These delete calls broadcast OnVersionChange (if needed) but were blocked |
+ // by open connections. |
+ if (!IsDeleteDatabaseBlocked()) { |
+ std::list<std::unique_ptr<PendingDeleteCall>> blocked_delete_calls; |
+ blocked_delete_calls_.swap(blocked_delete_calls); |
+ while (!blocked_delete_calls.empty()) { |
// Only the first delete call will delete the database, but each must fire |
// callbacks. |
std::unique_ptr<PendingDeleteCall> pending_delete_call( |
- pending_delete_calls.front()); |
- pending_delete_calls.pop_front(); |
+ std::move(blocked_delete_calls.front())); |
+ blocked_delete_calls.pop_front(); |
DeleteDatabaseFinal(pending_delete_call->callbacks()); |
} |
- // delete_database_final should never re-queue calls. |
- DCHECK(pending_delete_calls_.empty()); |
+ // DeleteDatabaseFinal should never re-queue these calls. |
+ DCHECK(blocked_delete_calls_.empty()); |
// Fall through when complete, as pending opens may be unblocked. |
} |
+ // These open calls were blocked by a pending/running upgrade or a pending |
+ // delete. |
if (!IsOpenConnectionBlocked()) { |
std::queue<IndexedDBPendingConnection> pending_open_calls; |
pending_open_calls_.swap(pending_open_calls); |
while (!pending_open_calls.empty()) { |
+ // This may re-queue open calls if an upgrade is necessary. |
OpenConnection(pending_open_calls.front()); |
pending_open_calls.pop(); |
} |
@@ -1694,18 +1722,22 @@ void IndexedDBDatabase::TransactionCreated(IndexedDBTransaction* transaction) { |
transactions_[transaction->id()] = transaction; |
} |
+bool IndexedDBDatabase::IsUpgradeRunning() const { |
+ return transaction_coordinator_.IsRunningVersionChangeTransaction(); |
+} |
+ |
+bool IndexedDBDatabase::IsUpgradePendingOrRunning() const { |
+ return pending_run_version_change_transaction_call_ || IsUpgradeRunning(); |
+} |
+ |
bool IndexedDBDatabase::IsOpenConnectionBlocked() const { |
- return !pending_delete_calls_.empty() || |
- transaction_coordinator_.IsRunningVersionChangeTransaction() || |
- pending_run_version_change_transaction_call_; |
+ return IsUpgradePendingOrRunning() || !blocked_delete_calls_.empty(); |
} |
void IndexedDBDatabase::OpenConnection( |
const IndexedDBPendingConnection& connection) { |
DCHECK(backing_store_.get()); |
- // TODO(jsbell): Should have a priority queue so that higher version |
- // requests are processed first. http://crbug.com/225850 |
if (IsOpenConnectionBlocked()) { |
// The backing store only detects data loss when it is first opened. The |
// presence of existing connections means we didn't even check for data loss |
@@ -1832,6 +1864,7 @@ void IndexedDBDatabase::RunVersionChangeTransactionFinal( |
object_store_ids, |
blink::WebIDBTransactionModeVersionChange); |
+ DCHECK(transaction_coordinator_.IsRunningVersionChangeTransaction()); |
transactions_[transaction_id]->ScheduleTask( |
base::Bind(&IndexedDBDatabase::VersionChangeOperation, |
this, |
@@ -1843,6 +1876,13 @@ void IndexedDBDatabase::RunVersionChangeTransactionFinal( |
void IndexedDBDatabase::DeleteDatabase( |
scoped_refptr<IndexedDBCallbacks> callbacks) { |
+ // If there's a running upgrade, the delete calls should be deferred so that |
+ // "versionchange" is seen after "success" fires. |
+ if (IsUpgradeRunning()) { |
+ pending_delete_calls_.push_back( |
+ std::unique_ptr<PendingDeleteCall>(new PendingDeleteCall(callbacks))); |
+ return; |
+ } |
if (IsDeleteDatabaseBlocked()) { |
for (const auto* connection : connections_) { |
@@ -1854,14 +1894,15 @@ void IndexedDBDatabase::DeleteDatabase( |
// OnBlocked will be fired at the request when one of the other |
// connections acks that the OnVersionChange was ignored. |
- pending_delete_calls_.push_back(new PendingDeleteCall(callbacks)); |
+ blocked_delete_calls_.push_back( |
+ std::unique_ptr<PendingDeleteCall>(new PendingDeleteCall(callbacks))); |
return; |
} |
DeleteDatabaseFinal(callbacks); |
} |
bool IndexedDBDatabase::IsDeleteDatabaseBlocked() const { |
- return !!ConnectionCount(); |
+ return IsUpgradePendingOrRunning() || !!ConnectionCount(); |
} |
void IndexedDBDatabase::DeleteDatabaseFinal( |
@@ -1900,15 +1941,15 @@ void IndexedDBDatabase::ForceClose() { |
} |
void IndexedDBDatabase::VersionChangeIgnored() { |
- if (pending_run_version_change_transaction_call_) |
+ if (pending_run_version_change_transaction_call_) { |
pending_run_version_change_transaction_call_->callbacks()->OnBlocked( |
metadata_.version); |
+ } |
- for (const auto& pending_delete_call : pending_delete_calls_) |
+ for (const auto& pending_delete_call : blocked_delete_calls_) |
pending_delete_call->callbacks()->OnBlocked(metadata_.version); |
} |
- |
void IndexedDBDatabase::Close(IndexedDBConnection* connection, bool forced) { |
DCHECK(connections_.count(connection)); |
DCHECK(connection->IsConnected()); |
@@ -1944,7 +1985,7 @@ void IndexedDBDatabase::Close(IndexedDBConnection* connection, bool forced) { |
// TODO(jsbell): Add a test for the pending_open_calls_ cases below. |
if (!ConnectionCount() && pending_open_calls_.empty() && |
- pending_delete_calls_.empty()) { |
+ blocked_delete_calls_.empty()) { |
DCHECK(transactions_.empty()); |
backing_store_ = NULL; |
factory_->ReleaseDatabase(identifier_, forced); |