| 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 |