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