| 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);
|
| }
|
| }
|
|
|