1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
|
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/webdata/web_database_service.h"
#include "base/bind.h"
#include "base/location.h"
#include "base/memory/scoped_vector.h"
#include "chrome/browser/api/webdata/web_data_results.h"
#include "chrome/browser/api/webdata/web_data_service_consumer.h"
#include "chrome/browser/webdata/web_data_request_manager.h"
// TODO(caitkp): Remove this autofill dependency.
#include "components/autofill/browser/autofill_country.h"
using base::Bind;
using base::FilePath;
using content::BrowserThread;
////////////////////////////////////////////////////////////////////////////////
//
// WebDataServiceBackend implementation.
//
////////////////////////////////////////////////////////////////////////////////
// Refcounted to allow asynchronous destruction on the DB thread.
class WebDataServiceBackend
: public base::RefCountedThreadSafe<WebDataServiceBackend,
BrowserThread::DeleteOnDBThread> {
public:
explicit WebDataServiceBackend(const FilePath& path);
// Must call only before InitDatabaseWithCallback.
void AddTable(scoped_ptr<WebDatabaseTable> table);
// Initializes the database and notifies caller via callback when complete.
// Callback is called synchronously.
void InitDatabaseWithCallback(
const WebDatabaseService::InitCallback& callback);
// Opens the database file from the profile path if an init has not yet been
// attempted. Separated from the constructor to ease construction/destruction
// of this object on one thread but database access on the DB thread. Returns
// the status of the database.
sql::InitStatus LoadDatabaseIfNecessary();
// Shuts down database. |should_reinit| tells us whether or not it should be
// possible to re-initialize the DB after the shutdown.
void ShutdownDatabase(bool should_reinit);
// Task wrappers to run database tasks.
void DBWriteTaskWrapper(
const WebDatabaseService::WriteTask& task,
scoped_ptr<WebDataRequest> request);
void DBReadTaskWrapper(
const WebDatabaseService::ReadTask& task,
scoped_ptr<WebDataRequest> request);
const scoped_refptr<WebDataRequestManager>& request_manager() {
return request_manager_;
}
WebDatabase* database() { return db_.get(); }
private:
friend struct BrowserThread::DeleteOnThread<BrowserThread::DB>;
friend class base::DeleteHelper<WebDataServiceBackend>;
virtual ~WebDataServiceBackend();
// Commit the current transaction.
void Commit();
// Path to database file.
FilePath db_path_;
// The tables that participate in managing the database. These are
// owned here but other than that this class does nothing with
// them. Their initialization is in whatever factory creates
// WebDatabaseService, and lookup by type is provided by the
// WebDatabase class. The tables need to be owned by this refcounted
// object, or they themselves would need to be refcounted. Owning
// them here rather than having WebDatabase own them makes for
// easier unit testing of WebDatabase.
ScopedVector<WebDatabaseTable> tables_;
scoped_ptr<WebDatabase> db_;
// Keeps track of all pending requests made to the db.
scoped_refptr<WebDataRequestManager> request_manager_;
// State of database initialization. Used to prevent the executing of tasks
// before the db is ready.
sql::InitStatus init_status_;
// True if an attempt has been made to load the database (even if the attempt
// fails), used to avoid continually trying to reinit if the db init fails.
bool init_complete_;
// The application locale. The locale is needed for some database migrations,
// and must be read on the UI thread. It's cached here so that we can pass it
// to the migration code on the DB thread.
const std::string app_locale_;
DISALLOW_COPY_AND_ASSIGN(WebDataServiceBackend);
};
WebDataServiceBackend::WebDataServiceBackend(const FilePath& path)
: db_path_(path),
request_manager_(new WebDataRequestManager()),
init_status_(sql::INIT_FAILURE),
init_complete_(false),
app_locale_(AutofillCountry::ApplicationLocale()) {
}
void WebDataServiceBackend::AddTable(scoped_ptr<WebDatabaseTable> table) {
DCHECK(!db_.get());
tables_.push_back(table.release());
}
void WebDataServiceBackend::InitDatabaseWithCallback(
const WebDatabaseService::InitCallback& callback) {
if (!callback.is_null()) {
callback.Run(LoadDatabaseIfNecessary());
}
}
sql::InitStatus WebDataServiceBackend::LoadDatabaseIfNecessary() {
if (init_complete_ || db_path_.empty()) {
return init_status_;
}
init_complete_ = true;
db_.reset(new WebDatabase());
for (ScopedVector<WebDatabaseTable>::iterator it = tables_.begin();
it != tables_.end();
++it) {
db_->AddTable(*it);
}
init_status_ = db_->Init(db_path_, app_locale_);
if (init_status_ != sql::INIT_OK) {
LOG(ERROR) << "Cannot initialize the web database: " << init_status_;
db_.reset(NULL);
return init_status_;
}
db_->BeginTransaction();
return init_status_;
}
void WebDataServiceBackend::ShutdownDatabase(bool should_reinit) {
if (db_ && init_status_ == sql::INIT_OK)
db_->CommitTransaction();
db_.reset(NULL);
init_complete_ = !should_reinit; // Setting init_complete_ to true will ensure
// that the init sequence is not re-run.
init_status_ = sql::INIT_FAILURE;
}
void WebDataServiceBackend::DBWriteTaskWrapper(
const WebDatabaseService::WriteTask& task,
scoped_ptr<WebDataRequest> request) {
LoadDatabaseIfNecessary();
if (db_ && init_status_ == sql::INIT_OK && !request->IsCancelled()) {
WebDatabase::State state = task.Run(db_.get());
if (state == WebDatabase::COMMIT_NEEDED)
Commit();
}
request_manager_->RequestCompleted(request.Pass());
}
void WebDataServiceBackend::DBReadTaskWrapper(
const WebDatabaseService::ReadTask& task,
scoped_ptr<WebDataRequest> request) {
LoadDatabaseIfNecessary();
if (db_ && init_status_ == sql::INIT_OK && !request->IsCancelled()) {
request->SetResult(task.Run(db_.get()).Pass());
}
request_manager_->RequestCompleted(request.Pass());
}
WebDataServiceBackend::~WebDataServiceBackend() {
ShutdownDatabase(false);
}
void WebDataServiceBackend::Commit() {
if (db_ && init_status_ == sql::INIT_OK) {
db_->CommitTransaction();
db_->BeginTransaction();
} else {
NOTREACHED() << "Commit scheduled after Shutdown()";
}
}
////////////////////////////////////////////////////////////////////////////////
WebDatabaseService::WebDatabaseService(const base::FilePath& path)
: path_(path) {
// WebDatabaseService should be instantiated on UI thread.
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// WebDatabaseService requires DB thread if instantiated.
DCHECK(BrowserThread::IsWellKnownThread(BrowserThread::DB));
}
WebDatabaseService::~WebDatabaseService() {
}
void WebDatabaseService::AddTable(scoped_ptr<WebDatabaseTable> table) {
if (!wds_backend_) {
wds_backend_ = new WebDataServiceBackend(path_);
}
wds_backend_->AddTable(table.Pass());
}
void WebDatabaseService::LoadDatabase(const InitCallback& callback) {
DCHECK(wds_backend_);
BrowserThread::PostTask(
BrowserThread::DB,
FROM_HERE,
Bind(&WebDataServiceBackend::InitDatabaseWithCallback,
wds_backend_, callback));
}
void WebDatabaseService::UnloadDatabase() {
if (!wds_backend_)
return;
BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
Bind(&WebDataServiceBackend::ShutdownDatabase,
wds_backend_, true));
}
void WebDatabaseService::ShutdownDatabase() {
if (!wds_backend_)
return;
BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
Bind(&WebDataServiceBackend::ShutdownDatabase,
wds_backend_, false));
}
WebDatabase* WebDatabaseService::GetDatabaseOnDB() const {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
if (!wds_backend_)
return NULL;
return wds_backend_->database();
}
void WebDatabaseService::ScheduleDBTask(
const tracked_objects::Location& from_here,
const WriteTask& task) {
if (!wds_backend_) {
NOTREACHED() << "Task scheduled after Shutdown()";
return;
}
scoped_ptr<WebDataRequest> request(
new WebDataRequest(NULL, wds_backend_->request_manager()));
BrowserThread::PostTask(BrowserThread::DB, from_here,
Bind(&WebDataServiceBackend::DBWriteTaskWrapper, wds_backend_,
task, base::Passed(&request)));
}
WebDataServiceBase::Handle WebDatabaseService::ScheduleDBTaskWithResult(
const tracked_objects::Location& from_here,
const ReadTask& task,
WebDataServiceConsumer* consumer) {
DCHECK(consumer);
WebDataServiceBase::Handle handle = 0;
if (!wds_backend_) {
NOTREACHED() << "Task scheduled after Shutdown()";
return handle;
}
scoped_ptr<WebDataRequest> request(
new WebDataRequest(consumer, wds_backend_->request_manager()));
handle = request->GetHandle();
BrowserThread::PostTask(BrowserThread::DB, from_here,
Bind(&WebDataServiceBackend::DBReadTaskWrapper, wds_backend_,
task, base::Passed(&request)));
return handle;
}
void WebDatabaseService::CancelRequest(WebDataServiceBase::Handle h) {
if (!wds_backend_)
return;
wds_backend_->request_manager()->CancelRequest(h);
}
|