1 // Copyright 2013 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.h"
6 
7 #include <utility>
8 
9 #include "base/logging.h"
10 #include "base/stl_util.h"
11 #include "content/browser/indexed_db/indexed_db_class_factory.h"
12 #include "content/browser/indexed_db/indexed_db_database_callbacks.h"
13 #include "content/browser/indexed_db/indexed_db_database_error.h"
14 #include "content/browser/indexed_db/indexed_db_factory_impl.h"
15 #include "content/browser/indexed_db/indexed_db_observer.h"
16 #include "content/browser/indexed_db/indexed_db_origin_state.h"
17 #include "content/browser/indexed_db/indexed_db_tracing.h"
18 #include "content/browser/indexed_db/indexed_db_transaction.h"
19 #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
20 
21 namespace content {
22 
23 namespace {
24 
25 static int32_t g_next_indexed_db_connection_id;
26 
27 }  // namespace
28 
IndexedDBConnection(IndexedDBOriginStateHandle origin_state_handle,IndexedDBClassFactory * indexed_db_class_factory,base::WeakPtr<IndexedDBDatabase> database,base::RepeatingClosure on_version_change_ignored,base::OnceCallback<void (IndexedDBConnection *)> on_close,scoped_refptr<IndexedDBDatabaseCallbacks> callbacks)29 IndexedDBConnection::IndexedDBConnection(
30     IndexedDBOriginStateHandle origin_state_handle,
31     IndexedDBClassFactory* indexed_db_class_factory,
32     base::WeakPtr<IndexedDBDatabase> database,
33     base::RepeatingClosure on_version_change_ignored,
34     base::OnceCallback<void(IndexedDBConnection*)> on_close,
35     scoped_refptr<IndexedDBDatabaseCallbacks> callbacks)
36     : id_(g_next_indexed_db_connection_id++),
37       origin_state_handle_(std::move(origin_state_handle)),
38       indexed_db_class_factory_(indexed_db_class_factory),
39       database_(std::move(database)),
40       on_version_change_ignored_(std::move(on_version_change_ignored)),
41       on_close_(std::move(on_close)),
42       callbacks_(callbacks) {
43   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
44 }
45 
~IndexedDBConnection()46 IndexedDBConnection::~IndexedDBConnection() {
47   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
48   if (!IsConnected())
49     return;
50 
51   // TODO(dmurph): Enforce that IndexedDBConnection cannot have any transactions
52   // during destruction. This is likely the case during regular execution, but
53   // is definitely not the case in unit tests.
54 
55   leveldb::Status status =
56       AbortTransactionsAndClose(CloseErrorHandling::kAbortAllReturnLastError);
57   if (!status.ok())
58     origin_state_handle_.origin_state()->tear_down_callback().Run(status);
59 }
60 
AbortTransactionsAndClose(CloseErrorHandling error_handling)61 leveldb::Status IndexedDBConnection::AbortTransactionsAndClose(
62     CloseErrorHandling error_handling) {
63   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
64   if (!IsConnected())
65     return leveldb::Status::OK();
66 
67   DCHECK(database_);
68   active_observers_.clear();
69   callbacks_ = nullptr;
70 
71   // Finish up any transaction, in case there were any running.
72   IndexedDBDatabaseError error(blink::mojom::IDBException::kUnknownError,
73                                "Connection is closing.");
74   leveldb::Status status;
75   switch (error_handling) {
76     case CloseErrorHandling::kReturnOnFirstError:
77       status = AbortAllTransactions(error);
78       break;
79     case CloseErrorHandling::kAbortAllReturnLastError:
80       status = AbortAllTransactionsAndIgnoreErrors(error);
81       break;
82   }
83 
84   std::move(on_close_).Run(this);
85   origin_state_handle_.Release();
86   active_observers_.clear();
87   return status;
88 }
89 
CloseAndReportForceClose()90 leveldb::Status IndexedDBConnection::CloseAndReportForceClose() {
91   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
92   if (!IsConnected())
93     return leveldb::Status::OK();
94 
95   scoped_refptr<IndexedDBDatabaseCallbacks> callbacks(callbacks_);
96   leveldb::Status last_error =
97       AbortTransactionsAndClose(CloseErrorHandling::kAbortAllReturnLastError);
98   callbacks->OnForcedClose();
99   return last_error;
100 }
101 
VersionChangeIgnored()102 void IndexedDBConnection::VersionChangeIgnored() {
103   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
104   on_version_change_ignored_.Run();
105 }
106 
IsConnected()107 bool IndexedDBConnection::IsConnected() {
108   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
109   return callbacks_.get();
110 }
111 
112 // The observers begin listening to changes only once they are activated.
ActivatePendingObservers(std::vector<std::unique_ptr<IndexedDBObserver>> pending_observers)113 void IndexedDBConnection::ActivatePendingObservers(
114     std::vector<std::unique_ptr<IndexedDBObserver>> pending_observers) {
115   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
116   for (auto& observer : pending_observers) {
117     active_observers_.push_back(std::move(observer));
118   }
119   pending_observers.clear();
120 }
121 
RemoveObservers(const std::vector<int32_t> & observer_ids_to_remove)122 void IndexedDBConnection::RemoveObservers(
123     const std::vector<int32_t>& observer_ids_to_remove) {
124   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
125   std::vector<int32_t> pending_observer_ids;
126   for (int32_t id_to_remove : observer_ids_to_remove) {
127     const auto& it = std::find_if(
128         active_observers_.begin(), active_observers_.end(),
129         [&id_to_remove](const std::unique_ptr<IndexedDBObserver>& o) {
130           return o->id() == id_to_remove;
131         });
132     if (it != active_observers_.end())
133       active_observers_.erase(it);
134     else
135       pending_observer_ids.push_back(id_to_remove);
136   }
137   if (pending_observer_ids.empty())
138     return;
139 
140   for (const auto& it : transactions_) {
141     it.second->RemovePendingObservers(pending_observer_ids);
142   }
143 }
144 
CreateTransaction(int64_t id,const std::set<int64_t> & scope,blink::mojom::IDBTransactionMode mode,IndexedDBBackingStore::Transaction * backing_store_transaction)145 IndexedDBTransaction* IndexedDBConnection::CreateTransaction(
146     int64_t id,
147     const std::set<int64_t>& scope,
148     blink::mojom::IDBTransactionMode mode,
149     IndexedDBBackingStore::Transaction* backing_store_transaction) {
150   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
151   CHECK_EQ(GetTransaction(id), nullptr) << "Duplicate transaction id." << id;
152   IndexedDBOriginState* origin_state = origin_state_handle_.origin_state();
153   std::unique_ptr<IndexedDBTransaction> transaction =
154       indexed_db_class_factory_->CreateIndexedDBTransaction(
155           id, this, scope, mode, database()->tasks_available_callback(),
156           origin_state ? origin_state->tear_down_callback()
157                        : IndexedDBTransaction::TearDownCallback(),
158           backing_store_transaction);
159   IndexedDBTransaction* transaction_ptr = transaction.get();
160   transactions_[id] = std::move(transaction);
161   return transaction_ptr;
162 }
163 
AbortTransactionAndTearDownOnError(IndexedDBTransaction * transaction,const IndexedDBDatabaseError & error)164 void IndexedDBConnection::AbortTransactionAndTearDownOnError(
165     IndexedDBTransaction* transaction,
166     const IndexedDBDatabaseError& error) {
167   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
168   IDB_TRACE1("IndexedDBDatabase::Abort(error)", "txn.id", transaction->id());
169   leveldb::Status status = transaction->Abort(error);
170   if (!status.ok())
171     origin_state_handle_.origin_state()->tear_down_callback().Run(status);
172 }
173 
AbortAllTransactions(const IndexedDBDatabaseError & error)174 leveldb::Status IndexedDBConnection::AbortAllTransactions(
175     const IndexedDBDatabaseError& error) {
176   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
177   for (const auto& pair : transactions_) {
178     auto& transaction = pair.second;
179     if (transaction->state() != IndexedDBTransaction::FINISHED) {
180       IDB_TRACE1("IndexedDBDatabase::Abort(error)", "transaction.id",
181                  transaction->id());
182       leveldb::Status status = transaction->Abort(error);
183       if (!status.ok())
184         return status;
185     }
186   }
187   return leveldb::Status::OK();
188 }
189 
AbortAllTransactionsAndIgnoreErrors(const IndexedDBDatabaseError & error)190 leveldb::Status IndexedDBConnection::AbortAllTransactionsAndIgnoreErrors(
191     const IndexedDBDatabaseError& error) {
192   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
193   leveldb::Status last_error;
194   for (const auto& pair : transactions_) {
195     auto& transaction = pair.second;
196     if (transaction->state() != IndexedDBTransaction::FINISHED) {
197       IDB_TRACE1("IndexedDBDatabase::Abort(error)", "transaction.id",
198                  transaction->id());
199       leveldb::Status status = transaction->Abort(error);
200       if (!status.ok())
201         last_error = status;
202     }
203   }
204   return last_error;
205 }
206 
GetTransaction(int64_t id) const207 IndexedDBTransaction* IndexedDBConnection::GetTransaction(int64_t id) const {
208   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
209   auto it = transactions_.find(id);
210   if (it == transactions_.end())
211     return nullptr;
212   return it->second.get();
213 }
214 
215 base::WeakPtr<IndexedDBTransaction>
AddTransactionForTesting(std::unique_ptr<IndexedDBTransaction> transaction)216 IndexedDBConnection::AddTransactionForTesting(
217     std::unique_ptr<IndexedDBTransaction> transaction) {
218   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
219   DCHECK(!base::Contains(transactions_, transaction->id()));
220   base::WeakPtr<IndexedDBTransaction> transaction_ptr =
221       transaction->ptr_factory_.GetWeakPtr();
222   transactions_[transaction->id()] = std::move(transaction);
223   return transaction_ptr;
224 }
225 
RemoveTransaction(int64_t id)226 void IndexedDBConnection::RemoveTransaction(int64_t id) {
227   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
228   transactions_.erase(id);
229 }
230 
ClearStateAfterClose()231 void IndexedDBConnection::ClearStateAfterClose() {
232   callbacks_ = nullptr;
233   active_observers_.clear();
234   origin_state_handle_.Release();
235 }
236 
237 }  // namespace content
238