Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1836)

Unified Diff: content/browser/indexed_db/indexed_db_database.cc

Issue 2148253003: IndexedDB: Implement explicit queue for IDBOpenDBRequests (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Use pending member, other review nits Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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 82f3609c0f90247815c1a307c79dab83657b4e1e..86ed058e8e5a6bec3beb6f7514f66e6b4b0adb90 100644
--- a/content/browser/indexed_db/indexed_db_database.cc
+++ b/content/browser/indexed_db/indexed_db_database.cc
@@ -77,61 +77,281 @@ HistogramIDBKeyPathType HistogramKeyPathType(const IndexedDBKeyPath& key_path) {
} // namespace
-// PendingUpgradeCall has a std::unique_ptr<IndexedDBConnection> because it owns
-// the
-// in-progress connection.
-class IndexedDBDatabase::PendingUpgradeCall {
+// This represents what script calls an 'IDBOpenDBRequest' - either a database
+// open or delete call. These may be blocked on other connections. After every
+// callback, the request must call IndexedDBDatabase::RequestComplete() or be
+// expecting a further callback.
+class IndexedDBDatabase::OpenOrDeleteRequest {
public:
- PendingUpgradeCall(scoped_refptr<IndexedDBCallbacks> callbacks,
- std::unique_ptr<IndexedDBConnection> connection,
- int64_t transaction_id,
- int64_t version)
- : callbacks_(callbacks),
- connection_(std::move(connection)),
- version_(version),
- transaction_id_(transaction_id) {}
- scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
- // Takes ownership of the connection object.
- std::unique_ptr<IndexedDBConnection> ReleaseConnection() WARN_UNUSED_RESULT {
- return std::move(connection_);
- }
- int64_t version() const { return version_; }
- int64_t transaction_id() const { return transaction_id_; }
+ explicit OpenOrDeleteRequest(scoped_refptr<IndexedDBDatabase> db) : db_(db) {}
+
+ virtual ~OpenOrDeleteRequest() {}
+
+ // Called when the request makes it to the front of the queue.
+ virtual void Perform() = 0;
+
+ // Called if a front-end signals that it is ignoring a "versionchange"
+ // event. This should result in firing a "blocked" event at the request.
+ virtual void OnVersionChangeIgnored() const = 0;
+
+ // Called when a connection is closed; if it corresponds to this connection,
+ // need to do cleanup. Otherwise, it may unblock further steps.
+ virtual void OnConnectionClosed(IndexedDBConnection* connection) = 0;
+
+ // Called when the upgrade transaction has started executing.
+ virtual void UpgradeTransactionStarted(int64_t old_version) = 0;
+
+ // Called when the upgrade transaction has finished.
+ virtual void UpgradeTransactionFinished(bool committed) = 0;
+
+ protected:
+ scoped_refptr<IndexedDBDatabase> db_;
private:
- scoped_refptr<IndexedDBCallbacks> callbacks_;
- std::unique_ptr<IndexedDBConnection> connection_;
- int64_t version_;
- const int64_t transaction_id_;
+ DISALLOW_COPY_AND_ASSIGN(OpenOrDeleteRequest);
};
-// PendingSuccessCall has a IndexedDBConnection* because the connection is now
-// owned elsewhere, but we need to cancel the success call if that connection
-// closes before it is sent.
-class IndexedDBDatabase::PendingSuccessCall {
+class IndexedDBDatabase::OpenRequest
+ : public IndexedDBDatabase::OpenOrDeleteRequest {
public:
- PendingSuccessCall(scoped_refptr<IndexedDBCallbacks> callbacks,
- IndexedDBConnection* connection,
- int64_t version)
- : callbacks_(callbacks), connection_(connection), version_(version) {}
- scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
- IndexedDBConnection* connection() const { return connection_; }
- int64_t version() const { return version_; }
+ OpenRequest(scoped_refptr<IndexedDBDatabase> db,
+ const IndexedDBPendingConnection& pending_connection)
+ : OpenOrDeleteRequest(db), pending_(pending_connection) {}
+
+ void Perform() override {
+ if (db_->metadata_.id == kInvalidId) {
+ // The database was deleted then immediately re-opened; OpenInternal()
+ // recreates it in the backing store.
+ if (!db_->OpenInternal().ok()) {
+ // TODO(jsbell): Consider including sanitized leveldb status message.
+ base::string16 message;
+ if (pending_.version == IndexedDBDatabaseMetadata::NO_VERSION) {
+ message = ASCIIToUTF16(
+ "Internal error opening database with no version specified.");
+ } else {
+ message =
+ ASCIIToUTF16("Internal error opening database with version ") +
+ Int64ToString16(pending_.version);
+ }
+ pending_.callbacks->OnError(IndexedDBDatabaseError(
+ blink::WebIDBDatabaseExceptionUnknownError, message));
+ db_->RequestComplete(this);
+ return;
+ }
+
+ DCHECK_EQ(IndexedDBDatabaseMetadata::NO_VERSION, db_->metadata_.version);
+ }
+
+ const int64_t old_version = db_->metadata_.version;
+ int64_t& new_version = pending_.version;
+
+ bool is_new_database = old_version == IndexedDBDatabaseMetadata::NO_VERSION;
+
+ if (new_version == IndexedDBDatabaseMetadata::DEFAULT_VERSION) {
+ // For unit tests only - skip upgrade steps. (Calling from script with
+ // DEFAULT_VERSION throws exception.)
+ DCHECK(is_new_database);
+ pending_.callbacks->OnSuccess(
+ db_->CreateConnection(pending_.database_callbacks,
+ pending_.child_process_id),
+ db_->metadata_);
+ db_->RequestComplete(this);
+ return;
+ }
+
+ if (!is_new_database &&
+ (new_version == old_version ||
+ new_version == IndexedDBDatabaseMetadata::NO_VERSION)) {
+ pending_.callbacks->OnSuccess(
+ db_->CreateConnection(pending_.database_callbacks,
+ pending_.child_process_id),
+ db_->metadata_);
+ db_->RequestComplete(this);
+ return;
+ }
+
+ if (new_version == IndexedDBDatabaseMetadata::NO_VERSION) {
+ // If no version is specified and no database exists, upgrade the
+ // database version to 1.
+ DCHECK(is_new_database);
+ new_version = 1;
+ } else if (new_version < old_version) {
+ // Requested version is lower than current version - fail the request.
+ DCHECK(!is_new_database);
+ pending_.callbacks->OnError(IndexedDBDatabaseError(
+ blink::WebIDBDatabaseExceptionVersionError,
+ ASCIIToUTF16("The requested version (") +
+ Int64ToString16(pending_.version) +
+ ASCIIToUTF16(") is less than the existing version (") +
+ Int64ToString16(db_->metadata_.version) + ASCIIToUTF16(").")));
+ db_->RequestComplete(this);
+ return;
+ }
+
+ // Requested version is higher than current version - upgrade needed.
+ DCHECK_GT(new_version, old_version);
+
+ if (db_->connections_.empty()) {
+ StartUpgrade();
+ return;
+ }
+
+ // There are outstanding connections - fire "versionchange" events and
+ // wait for the connections to close. Front end ensures the event is not
+ // fired at connections that have close_pending set. A "blocked" event
+ // will be fired at the request when one of the connections acks that the
+ // "versionchange" event was ignored.
+ DCHECK_NE(pending_.callbacks->data_loss_info().status,
+ blink::WebIDBDataLossTotal);
+ for (const auto* connection : db_->connections_)
+ connection->callbacks()->OnVersionChange(old_version, new_version);
+
+ // When all connections have closed the upgrade can proceed.
+ }
+
+ void OnVersionChangeIgnored() const override {
+ pending_.callbacks->OnBlocked(db_->metadata_.version);
+ }
+
+ void OnConnectionClosed(IndexedDBConnection* connection) override {
+ // This connection closed prematurely; signal an error and complete.
+ if (connection && connection->callbacks() == pending_.database_callbacks) {
+ pending_.callbacks->OnError(
+ IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError,
+ "The connection was closed."));
+ db_->RequestComplete(this);
+ return;
+ }
+
+ if (!db_->connections_.empty())
+ return;
+
+ StartUpgrade();
+ }
+
+ // Initiate the upgrade. The bulk of the work actually happens in
+ // IndexedDBDatabase::VersionChangeOperation in order to kick the
+ // transaction into the correct state.
+ void StartUpgrade() {
+ connection_ = db_->CreateConnection(pending_.database_callbacks,
+ pending_.child_process_id);
+ DCHECK_EQ(db_->connections_.count(connection_.get()), 1UL);
+
+ std::vector<int64_t> object_store_ids;
+ IndexedDBTransaction* transaction = db_->CreateTransaction(
+ pending_.transaction_id, connection_.get(), object_store_ids,
+ blink::WebIDBTransactionModeVersionChange);
+
+ DCHECK(db_->transaction_coordinator_.IsRunningVersionChangeTransaction());
+ transaction->ScheduleTask(
+ base::Bind(&IndexedDBDatabase::VersionChangeOperation, db_,
+ pending_.version, pending_.callbacks));
+ }
+
+ // Called when the upgrade transaction has started executing.
+ void UpgradeTransactionStarted(int64_t old_version) override {
+ DCHECK(connection_);
+ pending_.callbacks->OnUpgradeNeeded(old_version, std::move(connection_),
+ db_->metadata_);
+ }
+
+ void UpgradeTransactionFinished(bool committed) override {
+ // Ownership of connection was already passed along in OnUpgradeNeeded.
+ DCHECK(!connection_);
+
+ if (committed) {
+ DCHECK_EQ(pending_.version, db_->metadata_.version);
+ pending_.callbacks->OnSuccess(std::unique_ptr<IndexedDBConnection>(),
+ db_->metadata());
+ } else {
+ DCHECK_NE(pending_.version, db_->metadata_.version);
+ pending_.callbacks->OnError(
+ IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError,
+ "Version change transaction was aborted in "
+ "upgradeneeded event handler."));
+ }
+ db_->RequestComplete(this);
+ }
private:
- scoped_refptr<IndexedDBCallbacks> callbacks_;
- IndexedDBConnection* connection_;
- int64_t version_;
+ IndexedDBPendingConnection pending_;
+
+ // If an upgrade is needed, holds the pending connection until ownership is
+ // transferred to the IndexedDBDispatcherHost via OnUpgradeNeeded.
+ std::unique_ptr<IndexedDBConnection> connection_;
+
+ DISALLOW_COPY_AND_ASSIGN(OpenRequest);
};
-class IndexedDBDatabase::PendingDeleteCall {
+class IndexedDBDatabase::DeleteRequest
+ : public IndexedDBDatabase::OpenOrDeleteRequest {
public:
- explicit PendingDeleteCall(scoped_refptr<IndexedDBCallbacks> callbacks)
- : callbacks_(callbacks) {}
- scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
+ DeleteRequest(scoped_refptr<IndexedDBDatabase> db,
+ scoped_refptr<IndexedDBCallbacks> callbacks)
+ : OpenOrDeleteRequest(db), callbacks_(callbacks) {}
+
+ void Perform() override {
+ if (db_->connections_.empty()) {
+ // No connections, so delete immediately.
+ DoDelete();
+ return;
+ }
+
+ // Front end ensures the event is not fired at connections that have
+ // close_pending set.
+ const int64_t old_version = db_->metadata_.version;
+ const int64_t new_version = IndexedDBDatabaseMetadata::NO_VERSION;
+ DCHECK_NE(callbacks_->data_loss_info().status, blink::WebIDBDataLossTotal);
+ for (const auto* connection : db_->connections_)
+ connection->callbacks()->OnVersionChange(old_version, new_version);
+ }
+
+ void OnVersionChangeIgnored() const override {
+ callbacks_->OnBlocked(db_->metadata_.version);
+ }
+
+ void OnConnectionClosed(IndexedDBConnection* connection) override {
+ if (!db_->connections_.empty())
+ return;
+ DoDelete();
+ }
+
+ void DoDelete() {
+ leveldb::Status s =
+ db_->backing_store_->DeleteDatabase(db_->metadata_.name);
+ if (!s.ok()) {
+ // TODO(jsbell): Consider including sanitized leveldb status message.
+ IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
+ "Internal error deleting database.");
+ callbacks_->OnError(error);
+ if (s.IsCorruption()) {
+ url::Origin origin = db_->backing_store_->origin();
+ db_->backing_store_ = nullptr;
+ db_->factory_->HandleBackingStoreCorruption(origin, error);
+ }
+ db_->RequestComplete(this);
+ return;
+ }
+
+ int64_t old_version = db_->metadata_.version;
+ db_->metadata_.id = kInvalidId;
+ db_->metadata_.version = IndexedDBDatabaseMetadata::NO_VERSION;
+ db_->metadata_.max_object_store_id = kInvalidId;
+ db_->metadata_.object_stores.clear();
+ callbacks_->OnSuccess(old_version);
+ db_->factory_->DatabaseDeleted(db_->identifier_);
+
+ db_->RequestComplete(this);
+ }
+
+ void UpgradeTransactionStarted(int64_t old_version) override { NOTREACHED(); }
+
+ void UpgradeTransactionFinished(bool committed) override { NOTREACHED(); }
private:
scoped_refptr<IndexedDBCallbacks> callbacks_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeleteRequest);
};
scoped_refptr<IndexedDBDatabase> IndexedDBDatabase::Create(
@@ -231,9 +451,8 @@ leveldb::Status IndexedDBDatabase::OpenInternal() {
IndexedDBDatabase::~IndexedDBDatabase() {
DCHECK(transactions_.empty());
- DCHECK(pending_open_calls_.empty());
- DCHECK(pending_delete_calls_.empty());
- DCHECK(blocked_delete_calls_.empty());
+ DCHECK(!active_request_);
+ DCHECK(pending_requests_.empty());
}
size_t IndexedDBDatabase::GetMaxMessageSizeInBytes() const {
@@ -1529,7 +1748,6 @@ void IndexedDBDatabase::DeleteObjectStoreOperation(
void IndexedDBDatabase::VersionChangeOperation(
int64_t version,
scoped_refptr<IndexedDBCallbacks> callbacks,
- std::unique_ptr<IndexedDBConnection> connection,
IndexedDBTransaction* transaction) {
IDB_TRACE1(
"IndexedDBDatabase::VersionChangeOperation", "txn.id", transaction->id());
@@ -1553,10 +1771,7 @@ void IndexedDBDatabase::VersionChangeOperation(
metadata_.version));
metadata_.version = version;
- DCHECK(!pending_second_half_open_);
- pending_second_half_open_.reset(
- new PendingSuccessCall(callbacks, connection.get(), version));
- callbacks->OnUpgradeNeeded(old_version, std::move(connection), metadata());
+ active_request_->UpgradeTransactionStarted(old_version);
}
void IndexedDBDatabase::TransactionFinished(IndexedDBTransaction* transaction,
@@ -1566,27 +1781,12 @@ void IndexedDBDatabase::TransactionFinished(IndexedDBTransaction* transaction,
DCHECK_EQ(transactions_[transaction->id()], transaction);
transactions_.erase(transaction->id());
- if (transaction->mode() == blink::WebIDBTransactionModeVersionChange) {
- if (pending_second_half_open_) {
- if (committed) {
- DCHECK_EQ(pending_second_half_open_->version(), metadata_.version);
- DCHECK(metadata_.id != kInvalidId);
-
- // Connection was already minted for OnUpgradeNeeded callback.
- std::unique_ptr<IndexedDBConnection> connection;
- pending_second_half_open_->callbacks()->OnSuccess(std::move(connection),
- this->metadata());
- } else {
- pending_second_half_open_->callbacks()->OnError(
- IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError,
- "Version change transaction was aborted in "
- "upgradeneeded event handler."));
- }
- pending_second_half_open_.reset();
- }
-
- // Connection queue is now unblocked.
- ProcessPendingCalls();
+ // This may be an unrelated transaction finishing while waiting for
+ // connections to close, or the actual upgrade transaction from an active
+ // request. Notify the active request if it's the latter.
+ if (active_request_ &&
+ transaction->mode() == blink::WebIDBTransactionModeVersionChange) {
+ active_request_->UpgradeTransactionFinished(committed);
}
}
@@ -1600,100 +1800,42 @@ void IndexedDBDatabase::TransactionCommitFailed(const leveldb::Status& status) {
}
}
-size_t IndexedDBDatabase::ConnectionCount() const {
- // This does not include pending open calls, as those should not block version
- // changes and deletes.
- return connections_.size();
-}
+void IndexedDBDatabase::AppendRequest(
+ std::unique_ptr<OpenOrDeleteRequest> request) {
+ pending_requests_.push(std::move(request));
-size_t IndexedDBDatabase::PendingOpenCount() const {
- return pending_open_calls_.size();
+ if (!active_request_)
+ ProcessRequestQueue();
}
-size_t IndexedDBDatabase::PendingUpgradeCount() const {
- return pending_run_version_change_transaction_call_ ? 1 : 0;
-}
-
-size_t IndexedDBDatabase::RunningUpgradeCount() const {
- return pending_second_half_open_ ? 1 : 0;
-}
+void IndexedDBDatabase::RequestComplete(OpenOrDeleteRequest* request) {
+ DCHECK_EQ(request, active_request_.get());
+ active_request_.reset();
-size_t IndexedDBDatabase::PendingDeleteCount() const {
- return pending_delete_calls_.size();
+ if (!pending_requests_.empty())
+ ProcessRequestQueue();
}
-void IndexedDBDatabase::ProcessPendingCalls() {
- if (pending_run_version_change_transaction_call_ && ConnectionCount() == 1) {
- DCHECK(pending_run_version_change_transaction_call_->version() >
- metadata_.version);
- std::unique_ptr<PendingUpgradeCall> pending_call =
- std::move(pending_run_version_change_transaction_call_);
- RunVersionChangeTransactionFinal(pending_call->callbacks(),
- pending_call->ReleaseConnection(),
- pending_call->transaction_id(),
- pending_call->version());
- DCHECK_EQ(1u, ConnectionCount());
- // Fall through would be a no-op, since transaction must complete
- // asynchronously.
- DCHECK(IsUpgradeRunning());
- DCHECK(IsDeleteDatabaseBlocked());
- DCHECK(IsOpenConnectionBlocked());
+void IndexedDBDatabase::ProcessRequestQueue() {
+ // Don't run re-entrantly to avoid exploding call stacks for requests that
+ // complete synchronously. The loop below will process requests until one is
+ // blocked.
+ if (processing_pending_requests_)
return;
- }
- if (IsUpgradeRunning()) {
- DCHECK_EQ(ConnectionCount(), 1UL);
- return;
- }
+ DCHECK(!active_request_);
+ DCHECK(!pending_requests_.empty());
- // 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(
- std::move(blocked_delete_calls.front()));
- blocked_delete_calls.pop_front();
- DeleteDatabaseFinal(pending_delete_call->callbacks());
- }
- // 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();
- }
- }
+ base::AutoReset<bool> processing(&processing_pending_requests_, true);
+ do {
+ active_request_ = std::move(pending_requests_.front());
+ pending_requests_.pop();
+ active_request_->Perform();
+ // If the active request completed synchronously, keep going.
+ } while (!active_request_ && !pending_requests_.empty());
}
-void IndexedDBDatabase::CreateTransaction(
+IndexedDBTransaction* IndexedDBDatabase::CreateTransaction(
int64_t transaction_id,
IndexedDBConnection* connection,
const std::vector<int64_t>& object_store_ids,
@@ -1702,7 +1844,7 @@ void IndexedDBDatabase::CreateTransaction(
DCHECK(connections_.count(connection));
DCHECK(transactions_.find(transaction_id) == transactions_.end());
if (transactions_.find(transaction_id) != transactions_.end())
- return;
+ return nullptr;
UMA_HISTOGRAM_COUNTS_1000(
"WebCore.IndexedDB.Database.OutstandingTransactionCount",
@@ -1710,222 +1852,27 @@ void IndexedDBDatabase::CreateTransaction(
// The transaction will add itself to this database's coordinator, which
// manages the lifetime of the object.
- TransactionCreated(IndexedDBClassFactory::Get()->CreateIndexedDBTransaction(
- transaction_id, connection->GetWeakPtr(),
- std::set<int64_t>(object_store_ids.begin(), object_store_ids.end()), mode,
- new IndexedDBBackingStore::Transaction(backing_store_.get())));
+ IndexedDBTransaction* transaction =
+ IndexedDBClassFactory::Get()->CreateIndexedDBTransaction(
+ transaction_id, connection->GetWeakPtr(),
+ std::set<int64_t>(object_store_ids.begin(), object_store_ids.end()),
+ mode, new IndexedDBBackingStore::Transaction(backing_store_.get()));
+ TransactionCreated(transaction);
+ return transaction;
}
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 IsUpgradePendingOrRunning() || !blocked_delete_calls_.empty();
-}
-
void IndexedDBDatabase::OpenConnection(
const IndexedDBPendingConnection& connection) {
- DCHECK(backing_store_.get());
-
- 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
- // so there'd better not be any.
- DCHECK_NE(blink::WebIDBDataLossTotal,
- connection.callbacks->data_loss_info().status);
- pending_open_calls_.push(connection);
- return;
- }
-
- if (metadata_.id == kInvalidId) {
- // The database was deleted then immediately re-opened; OpenInternal()
- // recreates it in the backing store.
- if (OpenInternal().ok()) {
- DCHECK_EQ(IndexedDBDatabaseMetadata::NO_VERSION, metadata_.version);
- } else {
- base::string16 message;
- if (connection.version == IndexedDBDatabaseMetadata::NO_VERSION) {
- message = ASCIIToUTF16(
- "Internal error opening database with no version specified.");
- } else {
- message =
- ASCIIToUTF16("Internal error opening database with version ") +
- Int64ToString16(connection.version);
- }
- connection.callbacks->OnError(IndexedDBDatabaseError(
- blink::WebIDBDatabaseExceptionUnknownError, message));
- return;
- }
- }
-
- // We infer that the database didn't exist from its lack of either type of
- // version.
- bool is_new_database =
- metadata_.version == IndexedDBDatabaseMetadata::NO_VERSION;
-
- if (connection.version == IndexedDBDatabaseMetadata::DEFAULT_VERSION) {
- // For unit tests only - skip upgrade steps. Calling from script with
- // DEFAULT_VERSION throws exception.
- // TODO(jsbell): DCHECK that not in unit tests.
- DCHECK(is_new_database);
- connection.callbacks->OnSuccess(
- CreateConnection(connection.database_callbacks,
- connection.child_process_id),
- this->metadata());
- return;
- }
-
- // We may need to change the version.
- int64_t local_version = connection.version;
- if (local_version == IndexedDBDatabaseMetadata::NO_VERSION) {
- if (!is_new_database) {
- connection.callbacks->OnSuccess(
- CreateConnection(connection.database_callbacks,
- connection.child_process_id),
- this->metadata());
- return;
- }
- // Spec says: If no version is specified and no database exists, set
- // database version to 1.
- local_version = 1;
- }
-
- if (local_version > metadata_.version) {
- RunVersionChangeTransaction(connection.callbacks,
- CreateConnection(connection.database_callbacks,
- connection.child_process_id),
- connection.transaction_id,
- local_version);
- return;
- }
- if (local_version < metadata_.version) {
- connection.callbacks->OnError(IndexedDBDatabaseError(
- blink::WebIDBDatabaseExceptionVersionError,
- ASCIIToUTF16("The requested version (") +
- Int64ToString16(local_version) +
- ASCIIToUTF16(") is less than the existing version (") +
- Int64ToString16(metadata_.version) + ASCIIToUTF16(").")));
- return;
- }
- DCHECK_EQ(local_version, metadata_.version);
- connection.callbacks->OnSuccess(
- CreateConnection(connection.database_callbacks,
- connection.child_process_id),
- this->metadata());
-}
-
-void IndexedDBDatabase::RunVersionChangeTransaction(
- scoped_refptr<IndexedDBCallbacks> callbacks,
- std::unique_ptr<IndexedDBConnection> connection,
- int64_t transaction_id,
- int64_t requested_version) {
- DCHECK(callbacks.get());
- DCHECK(connections_.count(connection.get()));
- if (ConnectionCount() > 1) {
- DCHECK_NE(blink::WebIDBDataLossTotal, callbacks->data_loss_info().status);
- // Front end ensures the event is not fired at connections that have
- // close_pending set.
- for (const auto* iter : connections_) {
- if (iter != connection.get()) {
- iter->callbacks()->OnVersionChange(metadata_.version,
- requested_version);
- }
- }
- // OnBlocked will be fired at the request when one of the other
- // connections acks that the OnVersionChange was ignored.
-
- DCHECK(!pending_run_version_change_transaction_call_);
- pending_run_version_change_transaction_call_.reset(new PendingUpgradeCall(
- callbacks, std::move(connection), transaction_id, requested_version));
- return;
- }
- RunVersionChangeTransactionFinal(callbacks, std::move(connection),
- transaction_id, requested_version);
-}
-
-void IndexedDBDatabase::RunVersionChangeTransactionFinal(
- scoped_refptr<IndexedDBCallbacks> callbacks,
- std::unique_ptr<IndexedDBConnection> connection,
- int64_t transaction_id,
- int64_t requested_version) {
- std::vector<int64_t> object_store_ids;
- CreateTransaction(transaction_id,
- connection.get(),
- object_store_ids,
- blink::WebIDBTransactionModeVersionChange);
-
- DCHECK(transaction_coordinator_.IsRunningVersionChangeTransaction());
- transactions_[transaction_id]->ScheduleTask(
- base::Bind(&IndexedDBDatabase::VersionChangeOperation,
- this,
- requested_version,
- callbacks,
- base::Passed(&connection)));
- DCHECK(!pending_second_half_open_);
+ AppendRequest(base::MakeUnique<OpenRequest>(this, connection));
}
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_) {
- // Front end ensures the event is not fired at connections that have
- // close_pending set.
- connection->callbacks()->OnVersionChange(
- metadata_.version, IndexedDBDatabaseMetadata::NO_VERSION);
- }
- // OnBlocked will be fired at the request when one of the other
- // connections acks that the OnVersionChange was ignored.
-
- blocked_delete_calls_.push_back(
- std::unique_ptr<PendingDeleteCall>(new PendingDeleteCall(callbacks)));
- return;
- }
- DeleteDatabaseFinal(callbacks);
-}
-
-bool IndexedDBDatabase::IsDeleteDatabaseBlocked() const {
- return IsUpgradePendingOrRunning() || !!ConnectionCount();
-}
-
-void IndexedDBDatabase::DeleteDatabaseFinal(
- scoped_refptr<IndexedDBCallbacks> callbacks) {
- DCHECK(!IsDeleteDatabaseBlocked());
- DCHECK(backing_store_.get());
- leveldb::Status s = backing_store_->DeleteDatabase(metadata_.name);
- if (!s.ok()) {
- IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
- "Internal error deleting database.");
- callbacks->OnError(error);
- if (s.IsCorruption()) {
- url::Origin origin = backing_store_->origin();
- backing_store_ = NULL;
- factory_->HandleBackingStoreCorruption(origin, error);
- }
- return;
- }
- int64_t old_version = metadata_.version;
- metadata_.id = kInvalidId;
- metadata_.version = IndexedDBDatabaseMetadata::NO_VERSION;
- metadata_.object_stores.clear();
- callbacks->OnSuccess(old_version);
- factory_->DatabaseDeleted(identifier_);
+ AppendRequest(base::MakeUnique<DeleteRequest>(this, callbacks));
}
void IndexedDBDatabase::ForceClose() {
@@ -1940,13 +1887,8 @@ void IndexedDBDatabase::ForceClose() {
}
void IndexedDBDatabase::VersionChangeIgnored() {
- if (pending_run_version_change_transaction_call_) {
- pending_run_version_change_transaction_call_->callbacks()->OnBlocked(
- metadata_.version);
- }
-
- for (const auto& pending_delete_call : blocked_delete_calls_)
- pending_delete_call->callbacks()->OnBlocked(metadata_.version);
+ if (active_request_)
+ active_request_->OnVersionChangeIgnored();
}
void IndexedDBDatabase::Close(IndexedDBConnection* connection, bool forced) {
@@ -1956,12 +1898,10 @@ void IndexedDBDatabase::Close(IndexedDBConnection* connection, bool forced) {
IDB_TRACE("IndexedDBDatabase::Close");
- connections_.erase(connection);
-
- // Abort outstanding transactions from the closing connection. This
- // can not happen if the close is requested by the connection itself
- // as the front-end defers the close until all transactions are
- // complete, but can occur on process termination or forced close.
+ // Abort outstanding transactions from the closing connection. This can not
+ // happen if the close is requested by the connection itself as the
+ // front-end defers the close until all transactions are complete, but can
+ // occur on process termination or forced close.
{
auto transactions(transactions_);
for (const auto& it : transactions) {
@@ -1972,21 +1912,23 @@ void IndexedDBDatabase::Close(IndexedDBConnection* connection, bool forced) {
}
}
- if (pending_second_half_open_ &&
- pending_second_half_open_->connection() == connection) {
- pending_second_half_open_->callbacks()->OnError(
- IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError,
- "The connection was closed."));
- pending_second_half_open_.reset();
- }
+ // Abort transactions before removing the connection; aborting may complete
+ // an upgrade, and thus allow the next open/delete requests to proceed. The
+ // new active_request_ should see the old connection count until explicitly
+ // notified below.
+ connections_.erase(connection);
- ProcessPendingCalls();
+ // Notify the active request, which may need to do cleanup or proceed with
+ // the operation. This may trigger other work, such as more connections or
+ // deletions, so |active_request_| itself may change.
+ if (active_request_)
+ active_request_->OnConnectionClosed(connection);
- // TODO(jsbell): Add a test for the pending_open_calls_ cases below.
- if (!ConnectionCount() && pending_open_calls_.empty() &&
- blocked_delete_calls_.empty()) {
+ // If there are no more connections (current, active, or pending), tell the
+ // factory to clean us up.
+ if (connections_.empty() && !active_request_ && pending_requests_.empty()) {
DCHECK(transactions_.empty());
- backing_store_ = NULL;
+ backing_store_ = nullptr;
factory_->ReleaseDatabase(identifier_, forced);
}
}
« no previous file with comments | « content/browser/indexed_db/indexed_db_database.h ('k') | content/browser/indexed_db/indexed_db_database_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698