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