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