1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/indexed_db/indexed_db_connection_coordinator.h"
6
7 #include <set>
8 #include <tuple>
9 #include <utility>
10 #include <vector>
11
12 #include "base/metrics/histogram_functions.h"
13 #include "base/metrics/histogram_macros.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "components/services/storage/indexed_db/scopes/leveldb_scope.h"
17 #include "components/services/storage/indexed_db/scopes/leveldb_scopes.h"
18 #include "components/services/storage/indexed_db/scopes/scopes_lock_manager.h"
19 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_database.h"
20 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_factory.h"
21 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_transaction.h"
22 #include "content/browser/indexed_db/indexed_db_callback_helpers.h"
23 #include "content/browser/indexed_db/indexed_db_callbacks.h"
24 #include "content/browser/indexed_db/indexed_db_database.h"
25 #include "content/browser/indexed_db/indexed_db_database_callbacks.h"
26 #include "content/browser/indexed_db/indexed_db_database_error.h"
27 #include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
28 #include "content/browser/indexed_db/indexed_db_origin_state.h"
29 #include "content/browser/indexed_db/indexed_db_reporting.h"
30 #include "third_party/blink/public/common/indexeddb/indexeddb_metadata.h"
31 #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
32 #include "third_party/leveldatabase/env_chromium.h"
33
34 using base::ASCIIToUTF16;
35 using base::NumberToString16;
36 using blink::IndexedDBDatabaseMetadata;
37 using leveldb::Status;
38
39 namespace content {
40 namespace {
41 enum class RequestState {
42 kNotStarted,
43 kPendingNoConnections,
44 kPendingLocks,
45 kPendingTransactionComplete,
46 kError,
47 kDone,
48 };
49 } // namespace
50
51 // This represents what script calls an 'IDBOpenDBRequest' - either a database
52 // open or delete call. These may be blocked on other connections. After every
53 // callback, the request must call
54 // IndexedDBConnectionCoordinator::RequestComplete() or be expecting a further
55 // callback.
56 class IndexedDBConnectionCoordinator::ConnectionRequest {
57 public:
ConnectionRequest(IndexedDBOriginStateHandle origin_state_handle,IndexedDBDatabase * db,IndexedDBConnectionCoordinator * connection_coordinator,TasksAvailableCallback tasks_available_callback)58 ConnectionRequest(IndexedDBOriginStateHandle origin_state_handle,
59 IndexedDBDatabase* db,
60 IndexedDBConnectionCoordinator* connection_coordinator,
61 TasksAvailableCallback tasks_available_callback)
62 : origin_state_handle_(std::move(origin_state_handle)),
63 db_(db),
64 connection_coordinator_(connection_coordinator),
65 tasks_available_callback_(std::move(tasks_available_callback)) {}
66
~ConnectionRequest()67 virtual ~ConnectionRequest() {}
68
69 // Called when the request makes it to the front of the queue. The state()
70 // will be checked after this call, so there is no need to run the
71 // |tasks_available_callback_|.
72 virtual void Perform(bool has_connections) = 0;
73
74 // Called if a front-end signals that it is ignoring a "versionchange"
75 // event. This should result in firing a "blocked" event at the request.
76 virtual void OnVersionChangeIgnored() const = 0;
77
78 // Called when a connection is closed; if it corresponds to this connection,
79 // need to do cleanup.
80 // Not called during a force close.
81 virtual void OnConnectionClosed(IndexedDBConnection* connection) = 0;
82
83 // Called when there are no connections to the database.
84 virtual void OnNoConnections() = 0;
85
86 // Called when the transaction should be bound.
87 virtual void CreateAndBindTransaction() = 0;
88
89 // Called when the upgrade transaction has started executing.
90 virtual void UpgradeTransactionStarted(int64_t old_version) = 0;
91
92 // Called when the upgrade transaction has finished.
93 virtual void UpgradeTransactionFinished(bool committed) = 0;
94
95 // Called on all pending tasks during a force close. Returns if the task
96 // should be pruned (removed) from the task queue during the force close and
97 // any leveldb error that may have occurred while shutting down the connection
98 // request.
99 virtual std::tuple<bool, leveldb::Status> ShouldPruneForForceClose() = 0;
100
state() const101 RequestState state() const { return state_; }
102
103 // Relevant if state() is kError.
status() const104 leveldb::Status status() const { return saved_leveldb_status_; }
105
106 protected:
107 RequestState state_ = RequestState::kNotStarted;
108
109 IndexedDBOriginStateHandle origin_state_handle_;
110 // This is safe because IndexedDBDatabase owns this object.
111 IndexedDBDatabase* db_;
112
113 // Rawptr safe because IndexedDBConnectionCoordinator owns this object.
114 IndexedDBConnectionCoordinator* connection_coordinator_;
115
116 TasksAvailableCallback tasks_available_callback_;
117
118 leveldb::Status saved_leveldb_status_;
119
120 private:
121 DISALLOW_COPY_AND_ASSIGN(ConnectionRequest);
122 };
123
124 class IndexedDBConnectionCoordinator::OpenRequest
125 : public IndexedDBConnectionCoordinator::ConnectionRequest {
126 public:
OpenRequest(IndexedDBOriginStateHandle origin_state_handle,IndexedDBDatabase * db,std::unique_ptr<IndexedDBPendingConnection> pending_connection,IndexedDBConnectionCoordinator * connection_coordinator,TasksAvailableCallback tasks_available_callback)127 OpenRequest(IndexedDBOriginStateHandle origin_state_handle,
128 IndexedDBDatabase* db,
129 std::unique_ptr<IndexedDBPendingConnection> pending_connection,
130 IndexedDBConnectionCoordinator* connection_coordinator,
131 TasksAvailableCallback tasks_available_callback)
132 : ConnectionRequest(std::move(origin_state_handle),
133 db,
134 connection_coordinator,
135 std::move(tasks_available_callback)),
136 pending_(std::move(pending_connection)) {
137 db_->metadata_.was_cold_open = pending_->was_cold_open;
138 }
139
140 // Note: the |tasks_available_callback_| is NOT called here because the state
141 // is checked after this method.
Perform(bool has_connections)142 void Perform(bool has_connections) override {
143 if (db_->metadata().id == kInvalidDatabaseId) {
144 // The database was deleted then immediately re-opened; Open()
145 // recreates it in the backing store.
146 saved_leveldb_status_ = db_->OpenInternal();
147 if (!saved_leveldb_status_.ok()) {
148 // TODO(jsbell): Consider including sanitized leveldb status message.
149 base::string16 message;
150 if (pending_->version == IndexedDBDatabaseMetadata::NO_VERSION) {
151 message = ASCIIToUTF16(
152 "Internal error opening database with no version specified.");
153 } else {
154 message =
155 ASCIIToUTF16("Internal error opening database with version ") +
156 NumberToString16(pending_->version);
157 }
158 pending_->callbacks->OnError(IndexedDBDatabaseError(
159 blink::mojom::IDBException::kUnknownError, message));
160 state_ = RequestState::kError;
161 return;
162 }
163
164 DCHECK_EQ(IndexedDBDatabaseMetadata::NO_VERSION, db_->metadata().version);
165 }
166
167 const int64_t old_version = db_->metadata().version;
168 int64_t& new_version = pending_->version;
169
170 bool is_new_database = old_version == IndexedDBDatabaseMetadata::NO_VERSION;
171
172 if (new_version == IndexedDBDatabaseMetadata::DEFAULT_VERSION) {
173 // For unit tests only - skip upgrade steps. (Calling from script with
174 // DEFAULT_VERSION throws exception.)
175 DCHECK(is_new_database);
176 pending_->callbacks->OnSuccess(
177 db_->CreateConnection(std::move(origin_state_handle_),
178 pending_->database_callbacks),
179 db_->metadata_);
180 state_ = RequestState::kDone;
181 return;
182 }
183
184 if (!is_new_database &&
185 (new_version == old_version ||
186 new_version == IndexedDBDatabaseMetadata::NO_VERSION)) {
187 pending_->callbacks->OnSuccess(
188 db_->CreateConnection(std::move(origin_state_handle_),
189 pending_->database_callbacks),
190 db_->metadata_);
191 state_ = RequestState::kDone;
192 return;
193 }
194
195 if (new_version == IndexedDBDatabaseMetadata::NO_VERSION) {
196 // If no version is specified and no database exists, upgrade the
197 // database version to 1.
198 DCHECK(is_new_database);
199 new_version = 1;
200 } else if (new_version < old_version) {
201 // Requested version is lower than current version - fail the request.
202 DCHECK(!is_new_database);
203 pending_->callbacks->OnError(IndexedDBDatabaseError(
204 blink::mojom::IDBException::kVersionError,
205 ASCIIToUTF16("The requested version (") +
206 NumberToString16(pending_->version) +
207 ASCIIToUTF16(") is less than the existing version (") +
208 NumberToString16(db_->metadata_.version) + ASCIIToUTF16(").")));
209 state_ = RequestState::kDone;
210 return;
211 }
212
213 // Requested version is higher than current version - upgrade needed.
214 DCHECK_GT(new_version, old_version);
215
216 if (!has_connections) {
217 std::vector<ScopesLockManager::ScopeLockRequest> lock_requests = {
218 {kDatabaseRangeLockLevel, GetDatabaseLockRange(db_->metadata_.id),
219 ScopesLockManager::LockType::kExclusive}};
220 state_ = RequestState::kPendingLocks;
221 db_->lock_manager_->AcquireLocks(
222 std::move(lock_requests), lock_receiver_.weak_factory.GetWeakPtr(),
223 base::BindOnce(
224 &IndexedDBConnectionCoordinator::OpenRequest::StartUpgrade,
225 weak_factory_.GetWeakPtr()));
226 return;
227 }
228
229 // There are outstanding connections - fire "versionchange" events and
230 // wait for the connections to close. Front end ensures the event is not
231 // fired at connections that have close_pending set. A "blocked" event
232 // will be fired at the request when one of the connections acks that the
233 // "versionchange" event was ignored.
234 DCHECK_NE(pending_->data_loss_info.status,
235 blink::mojom::IDBDataLoss::Total);
236 state_ = RequestState::kPendingNoConnections;
237 db_->SendVersionChangeToAllConnections(old_version, new_version);
238
239 // When all connections have closed the upgrade can proceed.
240 }
241
OnVersionChangeIgnored() const242 void OnVersionChangeIgnored() const override {
243 DCHECK(state_ == RequestState::kPendingNoConnections);
244 pending_->callbacks->OnBlocked(db_->metadata_.version);
245 }
246
OnConnectionClosed(IndexedDBConnection * connection)247 void OnConnectionClosed(IndexedDBConnection* connection) override {
248 // This connection closed prematurely; signal an error and complete.
249 if (connection == connection_ptr_for_close_comparision_) {
250 connection_ptr_for_close_comparision_ = nullptr;
251 if (!pending_->callbacks->is_complete()) {
252 pending_->callbacks->OnError(
253 IndexedDBDatabaseError(blink::mojom::IDBException::kAbortError,
254 "The connection was closed."));
255 }
256 state_ = RequestState::kDone;
257 tasks_available_callback_.Run();
258 return;
259 }
260 }
261
OnNoConnections()262 void OnNoConnections() override {
263 DCHECK(state_ == RequestState::kPendingNoConnections);
264 std::vector<ScopesLockManager::ScopeLockRequest> lock_requests = {
265 {kDatabaseRangeLockLevel, GetDatabaseLockRange(db_->metadata().id),
266 ScopesLockManager::LockType::kExclusive}};
267 state_ = RequestState::kPendingLocks;
268 db_->lock_manager_->AcquireLocks(
269 std::move(lock_requests), lock_receiver_.weak_factory.GetWeakPtr(),
270 base::BindOnce(
271 &IndexedDBConnectionCoordinator::OpenRequest::StartUpgrade,
272 weak_factory_.GetWeakPtr()));
273 }
274
275 // Initiate the upgrade. The bulk of the work actually happens in
276 // IndexedDBConnectionCoordinator::VersionChangeOperation in order to kick the
277 // transaction into the correct state.
StartUpgrade()278 void StartUpgrade() {
279 DCHECK(state_ == RequestState::kPendingLocks);
280
281 DCHECK(!lock_receiver_.locks.empty());
282 connection_ = db_->CreateConnection(std::move(origin_state_handle_),
283 pending_->database_callbacks);
284 DCHECK(!connection_ptr_for_close_comparision_);
285 connection_ptr_for_close_comparision_ = connection_.get();
286 DCHECK_EQ(db_->connections().count(connection_.get()), 1UL);
287
288 std::vector<int64_t> object_store_ids;
289
290 state_ = RequestState::kPendingTransactionComplete;
291 IndexedDBTransaction* transaction = connection_->CreateTransaction(
292 pending_->transaction_id,
293 std::set<int64_t>(object_store_ids.begin(), object_store_ids.end()),
294 blink::mojom::IDBTransactionMode::VersionChange,
295 db_->backing_store()
296 ->CreateTransaction(blink::mojom::IDBTransactionDurability::Strict,
297 blink::mojom::IDBTransactionMode::ReadWrite)
298 .release());
299
300 // Save a WeakPtr<IndexedDBTransaction> for the CreateAndBindTransaction
301 // function to use later.
302 pending_->transaction = transaction->AsWeakPtr();
303
304 transaction->ScheduleTask(BindWeakOperation(
305 &IndexedDBDatabase::VersionChangeOperation, db_->AsWeakPtr(),
306 pending_->version, pending_->callbacks));
307 transaction->mutable_locks_receiver()->locks =
308 std::move(lock_receiver_.locks);
309 transaction->Start();
310 }
311
CreateAndBindTransaction()312 void CreateAndBindTransaction() override {
313 if (pending_->create_transaction_callback && pending_->transaction) {
314 std::move(pending_->create_transaction_callback)
315 .Run(std::move(pending_->transaction));
316 }
317 }
318
319 // Called when the upgrade transaction has started executing.
UpgradeTransactionStarted(int64_t old_version)320 void UpgradeTransactionStarted(int64_t old_version) override {
321 DCHECK(state_ == RequestState::kPendingTransactionComplete);
322 DCHECK(connection_);
323 pending_->callbacks->OnUpgradeNeeded(old_version, std::move(connection_),
324 db_->metadata_,
325 pending_->data_loss_info);
326 }
327
UpgradeTransactionFinished(bool committed)328 void UpgradeTransactionFinished(bool committed) override {
329 DCHECK(state_ == RequestState::kPendingTransactionComplete);
330 // Ownership of connection was already passed along in OnUpgradeNeeded.
331 if (committed) {
332 DCHECK_EQ(pending_->version, db_->metadata_.version);
333 pending_->callbacks->OnSuccess(nullptr, db_->metadata());
334 } else {
335 DCHECK_NE(pending_->version, db_->metadata_.version);
336 pending_->callbacks->OnError(
337 IndexedDBDatabaseError(blink::mojom::IDBException::kAbortError,
338 "Version change transaction was aborted in "
339 "upgradeneeded event handler."));
340 }
341 state_ = RequestState::kDone;
342 tasks_available_callback_.Run();
343 }
344
ShouldPruneForForceClose()345 std::tuple<bool, leveldb::Status> ShouldPruneForForceClose() override {
346 DCHECK(pending_);
347 if (!pending_->callbacks->is_complete()) {
348 pending_->callbacks->OnError(
349 IndexedDBDatabaseError(blink::mojom::IDBException::kAbortError,
350 "The connection was closed."));
351 }
352 if (state_ != RequestState::kError)
353 state_ = RequestState::kDone;
354
355 leveldb::Status status;
356 if (connection_) {
357 // CloseAndReportForceClose calls OnForcedClose on the database callbacks,
358 // so we don't need to.
359 status = connection_->CloseAndReportForceClose();
360 connection_.reset();
361 } else {
362 pending_->database_callbacks->OnForcedClose();
363 }
364 pending_.reset();
365 // The tasks_available_callback_ is NOT run here, because we are assuming
366 // the caller is doing their own cleanup & execution for ForceClose.
367 return std::make_tuple(true, status);
368 }
369
370 private:
371 ScopesLocksHolder lock_receiver_;
372
373 std::unique_ptr<IndexedDBPendingConnection> pending_;
374
375 // If an upgrade is needed, holds the pending connection until ownership is
376 // transferred to the IndexedDBDispatcherHost via OnUpgradeNeeded.
377 std::unique_ptr<IndexedDBConnection> connection_;
378
379 // This raw pointer is stored solely for comparison to the connection in
380 // OnConnectionClosed. It is not guaranteed to be pointing to a live object.
381 IndexedDBConnection* connection_ptr_for_close_comparision_ = nullptr;
382
383 base::WeakPtrFactory<OpenRequest> weak_factory_{this};
384 DISALLOW_COPY_AND_ASSIGN(OpenRequest);
385 };
386
387 class IndexedDBConnectionCoordinator::DeleteRequest
388 : public IndexedDBConnectionCoordinator::ConnectionRequest {
389 public:
DeleteRequest(IndexedDBOriginStateHandle origin_state_handle,IndexedDBDatabase * db,scoped_refptr<IndexedDBCallbacks> callbacks,base::OnceClosure on_database_deleted,IndexedDBConnectionCoordinator * connection_coordinator,TasksAvailableCallback tasks_available_callback)390 DeleteRequest(IndexedDBOriginStateHandle origin_state_handle,
391 IndexedDBDatabase* db,
392 scoped_refptr<IndexedDBCallbacks> callbacks,
393 base::OnceClosure on_database_deleted,
394 IndexedDBConnectionCoordinator* connection_coordinator,
395 TasksAvailableCallback tasks_available_callback)
396 : ConnectionRequest(std::move(origin_state_handle),
397 db,
398 connection_coordinator,
399 std::move(tasks_available_callback)),
400 callbacks_(callbacks),
401 on_database_deleted_(std::move(on_database_deleted)) {}
402
Perform(bool has_connections)403 void Perform(bool has_connections) override {
404 if (!has_connections) {
405 // No connections, so delete immediately.
406 std::vector<ScopesLockManager::ScopeLockRequest> lock_requests = {
407 {kDatabaseRangeLockLevel, GetDatabaseLockRange(db_->metadata().id),
408 ScopesLockManager::LockType::kExclusive}};
409 state_ = RequestState::kPendingLocks;
410 db_->lock_manager_->AcquireLocks(
411 std::move(lock_requests), lock_receiver_.AsWeakPtr(),
412 base::BindOnce(
413 &IndexedDBConnectionCoordinator::DeleteRequest::DoDelete,
414 weak_factory_.GetWeakPtr()));
415 return;
416 }
417
418 // Front end ensures the event is not fired at connections that have
419 // close_pending set.
420 const int64_t old_version = db_->metadata().version;
421 const int64_t new_version = IndexedDBDatabaseMetadata::NO_VERSION;
422 db_->SendVersionChangeToAllConnections(old_version, new_version);
423 state_ = RequestState::kPendingNoConnections;
424 }
425
OnVersionChangeIgnored() const426 void OnVersionChangeIgnored() const override {
427 DCHECK(state_ == RequestState::kPendingNoConnections);
428 callbacks_->OnBlocked(db_->metadata_.version);
429 }
430
OnConnectionClosed(IndexedDBConnection * connection)431 void OnConnectionClosed(IndexedDBConnection* connection) override {}
432
OnNoConnections()433 void OnNoConnections() override {
434 DCHECK(state_ == RequestState::kPendingNoConnections);
435 std::vector<ScopesLockManager::ScopeLockRequest> lock_requests = {
436 {kDatabaseRangeLockLevel, GetDatabaseLockRange(db_->metadata().id),
437 ScopesLockManager::LockType::kExclusive}};
438 state_ = RequestState::kPendingLocks;
439 db_->lock_manager_->AcquireLocks(
440 std::move(lock_requests), lock_receiver_.AsWeakPtr(),
441 base::BindOnce(&IndexedDBConnectionCoordinator::DeleteRequest::DoDelete,
442 weak_factory_.GetWeakPtr()));
443 }
444
DoDelete()445 void DoDelete() {
446 DCHECK(state_ == RequestState::kPendingLocks);
447 state_ = RequestState::kPendingTransactionComplete;
448 UMA_HISTOGRAM_ENUMERATION(
449 indexed_db::kBackingStoreActionUmaName,
450 indexed_db::IndexedDBAction::kDatabaseDeleteAttempt);
451 // This is used to check if this class is still alive after the destruction
452 // of the TransactionalLevelDBTransaction, which can synchronously cause the
453 // system to be shut down if the disk is really bad.
454 base::WeakPtr<DeleteRequest> weak_ptr = weak_factory_.GetWeakPtr();
455 if (db_->backing_store_) {
456 scoped_refptr<TransactionalLevelDBTransaction> txn;
457 TransactionalLevelDBDatabase* db = db_->backing_store_->db();
458 if (db) {
459 txn = db->class_factory()->CreateLevelDBTransaction(
460 db, db->scopes()->CreateScope(std::move(lock_receiver_.locks), {}));
461 txn->set_commit_cleanup_complete_callback(
462 std::move(on_database_deleted_));
463 }
464 saved_leveldb_status_ =
465 db_->backing_store_->DeleteDatabase(db_->metadata_.name, txn.get());
466 base::UmaHistogramEnumeration(
467 "WebCore.IndexedDB.BackingStore.DeleteDatabaseStatus",
468 leveldb_env::GetLevelDBStatusUMAValue(saved_leveldb_status_),
469 leveldb_env::LEVELDB_STATUS_MAX);
470 }
471
472 if (!weak_ptr)
473 return;
474
475 if (!saved_leveldb_status_.ok()) {
476 // TODO(jsbell): Consider including sanitized leveldb status message.
477 IndexedDBDatabaseError error(blink::mojom::IDBException::kUnknownError,
478 "Internal error deleting database.");
479 callbacks_->OnError(error);
480 state_ = RequestState::kError;
481 tasks_available_callback_.Run();
482 return;
483 }
484
485 int64_t old_version = db_->metadata_.version;
486 db_->metadata_.id = kInvalidDatabaseId;
487 db_->metadata_.version = IndexedDBDatabaseMetadata::NO_VERSION;
488 db_->metadata_.max_object_store_id = 0;
489 db_->metadata_.object_stores.clear();
490 // Unittests (specifically the IndexedDBDatabase unittests) can have the
491 // backing store be a nullptr, so report deleted here.
492 if (on_database_deleted_)
493 std::move(on_database_deleted_).Run();
494 callbacks_->OnSuccess(old_version);
495
496 state_ = RequestState::kDone;
497 tasks_available_callback_.Run();
498 }
499
CreateAndBindTransaction()500 void CreateAndBindTransaction() override { NOTREACHED(); }
501
UpgradeTransactionStarted(int64_t old_version)502 void UpgradeTransactionStarted(int64_t old_version) override { NOTREACHED(); }
503
UpgradeTransactionFinished(bool committed)504 void UpgradeTransactionFinished(bool committed) override { NOTREACHED(); }
505
506 // The delete requests should always be run during force close.
ShouldPruneForForceClose()507 std::tuple<bool, leveldb::Status> ShouldPruneForForceClose() override {
508 return std::make_tuple(false, leveldb::Status::OK());
509 }
510
511 private:
512 ScopesLocksHolder lock_receiver_;
513 scoped_refptr<IndexedDBCallbacks> callbacks_;
514 base::OnceClosure on_database_deleted_;
515
516 base::WeakPtrFactory<DeleteRequest> weak_factory_{this};
517 DISALLOW_COPY_AND_ASSIGN(DeleteRequest);
518 };
519
IndexedDBConnectionCoordinator(IndexedDBDatabase * db,TasksAvailableCallback tasks_available_callback)520 IndexedDBConnectionCoordinator::IndexedDBConnectionCoordinator(
521 IndexedDBDatabase* db,
522 TasksAvailableCallback tasks_available_callback)
523 : db_(db), tasks_available_callback_(std::move(tasks_available_callback)) {}
524 IndexedDBConnectionCoordinator::~IndexedDBConnectionCoordinator() = default;
525
ScheduleOpenConnection(IndexedDBOriginStateHandle origin_state_handle,std::unique_ptr<IndexedDBPendingConnection> connection)526 void IndexedDBConnectionCoordinator::ScheduleOpenConnection(
527 IndexedDBOriginStateHandle origin_state_handle,
528 std::unique_ptr<IndexedDBPendingConnection> connection) {
529 request_queue_.push(std::make_unique<OpenRequest>(
530 std::move(origin_state_handle), db_, std::move(connection), this,
531 tasks_available_callback_));
532 tasks_available_callback_.Run();
533 }
534
ScheduleDeleteDatabase(IndexedDBOriginStateHandle origin_state_handle,scoped_refptr<IndexedDBCallbacks> callbacks,base::OnceClosure on_deletion_complete)535 void IndexedDBConnectionCoordinator::ScheduleDeleteDatabase(
536 IndexedDBOriginStateHandle origin_state_handle,
537 scoped_refptr<IndexedDBCallbacks> callbacks,
538 base::OnceClosure on_deletion_complete) {
539 request_queue_.push(std::make_unique<DeleteRequest>(
540 std::move(origin_state_handle), db_, callbacks,
541 std::move(on_deletion_complete), this, tasks_available_callback_));
542 tasks_available_callback_.Run();
543 }
544
PruneTasksForForceClose()545 leveldb::Status IndexedDBConnectionCoordinator::PruneTasksForForceClose() {
546 // Remove all pending requests that don't want to execute during force close
547 // (open requests).
548 base::queue<std::unique_ptr<ConnectionRequest>> requests_to_still_run;
549 leveldb::Status last_error;
550 while (!request_queue_.empty()) {
551 std::unique_ptr<ConnectionRequest> request =
552 std::move(request_queue_.front());
553 request_queue_.pop();
554 bool prune;
555 leveldb::Status old_error = request->status();
556 leveldb::Status status;
557 std::tie(prune, status) = request->ShouldPruneForForceClose();
558
559 if (prune) {
560 if (!old_error.ok())
561 last_error = old_error;
562 request.reset();
563 } else {
564 requests_to_still_run.push(std::move(request));
565 }
566
567 if (!status.ok())
568 last_error = status;
569 }
570
571 request_queue_ = std::move(requests_to_still_run);
572 return last_error;
573 }
574
OnConnectionClosed(IndexedDBConnection * connection)575 void IndexedDBConnectionCoordinator::OnConnectionClosed(
576 IndexedDBConnection* connection) {
577 DCHECK(connection->database().get() == db_);
578
579 if (!request_queue_.empty())
580 request_queue_.front()->OnConnectionClosed(connection);
581 }
582
OnNoConnections()583 void IndexedDBConnectionCoordinator::OnNoConnections() {
584 if (request_queue_.empty() ||
585 request_queue_.front()->state() != RequestState::kPendingNoConnections) {
586 return;
587 }
588 request_queue_.front()->OnNoConnections();
589 }
590
591 // TODO(dmurph): Attach an ID to the connection change events to prevent
592 // mis-propogation to the wrong connection request.
OnVersionChangeIgnored()593 void IndexedDBConnectionCoordinator::OnVersionChangeIgnored() {
594 if (request_queue_.empty() ||
595 request_queue_.front()->state() != RequestState::kPendingNoConnections) {
596 return;
597 }
598 request_queue_.front()->OnVersionChangeIgnored();
599 }
600
OnUpgradeTransactionStarted(int64_t old_version)601 void IndexedDBConnectionCoordinator::OnUpgradeTransactionStarted(
602 int64_t old_version) {
603 if (request_queue_.empty() || request_queue_.front()->state() !=
604 RequestState::kPendingTransactionComplete) {
605 return;
606 }
607 request_queue_.front()->UpgradeTransactionStarted(old_version);
608 }
609
CreateAndBindUpgradeTransaction()610 void IndexedDBConnectionCoordinator::CreateAndBindUpgradeTransaction() {
611 if (request_queue_.empty() || request_queue_.front()->state() !=
612 RequestState::kPendingTransactionComplete) {
613 return;
614 }
615 request_queue_.front()->CreateAndBindTransaction();
616 }
617
OnUpgradeTransactionFinished(bool committed)618 void IndexedDBConnectionCoordinator::OnUpgradeTransactionFinished(
619 bool committed) {
620 if (request_queue_.empty() || request_queue_.front()->state() !=
621 RequestState::kPendingTransactionComplete) {
622 return;
623 }
624 request_queue_.front()->UpgradeTransactionFinished(committed);
625 }
626
627 std::tuple<IndexedDBConnectionCoordinator::ExecuteTaskResult, leveldb::Status>
ExecuteTask(bool has_connections)628 IndexedDBConnectionCoordinator::ExecuteTask(bool has_connections) {
629 if (request_queue_.empty())
630 return std::make_tuple(ExecuteTaskResult::kDone, leveldb::Status());
631
632 auto& request = request_queue_.front();
633 if (request->state() == RequestState::kNotStarted) {
634 request->Perform(has_connections);
635 DCHECK(request->state() != RequestState::kNotStarted);
636 }
637
638 switch (request->state()) {
639 case RequestState::kNotStarted:
640 NOTREACHED();
641 return std::make_tuple(ExecuteTaskResult::kError, leveldb::Status::OK());
642 case RequestState::kPendingNoConnections:
643 case RequestState::kPendingLocks:
644 case RequestState::kPendingTransactionComplete:
645 return std::make_tuple(ExecuteTaskResult::kPendingAsyncWork, leveldb::Status());
646 case RequestState::kDone: {
647 // Move |request_to_discard| out of |request_queue_| then
648 // |request_queue_.pop()|. We do this because |request_to_discard|'s dtor
649 // calls OnConnectionClosed and OnNoConnections, which interact with
650 // |request_queue_| assuming the queue no longer holds
651 // |request_to_discard|.
652 auto request_to_discard = std::move(request_queue_.front());
653 request_queue_.pop();
654 request_to_discard.reset();
655 return std::make_tuple(request_queue_.empty() ? ExecuteTaskResult::kDone
656 : ExecuteTaskResult::kMoreTasks,
657 leveldb::Status::OK());
658 }
659 case RequestState::kError: {
660 leveldb::Status status = request->status();
661 // Move |request_to_discard| out of |request_queue_| then
662 // |request_queue_.pop()|. We do this because |request_to_discard|'s dtor
663 // calls OnConnectionClosed and OnNoConnections, which interact with
664 // |request_queue_| assuming the queue no longer holds
665 // |request_to_discard|.
666 auto request_to_discard = std::move(request_queue_.front());
667 request_queue_.pop();
668 request_to_discard.reset();
669 return std::make_tuple(ExecuteTaskResult::kError, status);
670 }
671 }
672 NOTREACHED();
673 }
674
ActiveOpenDeleteCount() const675 size_t IndexedDBConnectionCoordinator::ActiveOpenDeleteCount() const {
676 return request_queue_.empty() ? 0 : 1;
677 }
678
679 // Number of open/delete calls that are waiting their turn.
PendingOpenDeleteCount() const680 size_t IndexedDBConnectionCoordinator::PendingOpenDeleteCount() const {
681 if (request_queue_.empty())
682 return 0;
683 if (request_queue_.front()->state() == RequestState::kNotStarted)
684 return request_queue_.size();
685 return request_queue_.size() - 1;
686 }
687
688 } // namespace content
689