Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/dom_storage/local_storage_context_mojo.h" | 5 #include "content/browser/dom_storage/local_storage_context_mojo.h" |
| 6 | 6 |
| 7 #include "base/memory/ptr_util.h" | 7 #include "base/memory/ptr_util.h" |
| 8 #include "base/strings/string_number_conversions.h" | 8 #include "base/strings/string_number_conversions.h" |
| 9 #include "components/leveldb/public/cpp/util.h" | 9 #include "components/leveldb/public/cpp/util.h" |
| 10 #include "components/leveldb/public/interfaces/leveldb.mojom.h" | 10 #include "components/leveldb/public/interfaces/leveldb.mojom.h" |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 43 std::vector<uint8_t> CreateMetaDataKey(const url::Origin& origin) { | 43 std::vector<uint8_t> CreateMetaDataKey(const url::Origin& origin) { |
| 44 auto serialized_origin = leveldb::StdStringToUint8Vector(origin.Serialize()); | 44 auto serialized_origin = leveldb::StdStringToUint8Vector(origin.Serialize()); |
| 45 std::vector<uint8_t> key; | 45 std::vector<uint8_t> key; |
| 46 key.reserve(arraysize(kMetaPrefix) + serialized_origin.size()); | 46 key.reserve(arraysize(kMetaPrefix) + serialized_origin.size()); |
| 47 key.insert(key.end(), kMetaPrefix, kMetaPrefix + arraysize(kMetaPrefix)); | 47 key.insert(key.end(), kMetaPrefix, kMetaPrefix + arraysize(kMetaPrefix)); |
| 48 key.insert(key.end(), serialized_origin.begin(), serialized_origin.end()); | 48 key.insert(key.end(), serialized_origin.begin(), serialized_origin.end()); |
| 49 return key; | 49 return key; |
| 50 } | 50 } |
| 51 | 51 |
| 52 void NoOpSuccess(bool success) {} | 52 void NoOpSuccess(bool success) {} |
| 53 } | 53 |
| 54 } // namespace | |
| 54 | 55 |
| 55 LocalStorageContextMojo::LocalStorageContextMojo( | 56 LocalStorageContextMojo::LocalStorageContextMojo( |
| 56 service_manager::Connector* connector, | 57 service_manager::Connector* connector, |
| 57 const base::FilePath& subdirectory) | 58 const base::FilePath& subdirectory) |
| 58 : connector_(connector), | 59 : connector_(connector), |
| 59 subdirectory_(subdirectory), | 60 subdirectory_(subdirectory), |
| 60 weak_ptr_factory_(this) {} | 61 weak_ptr_factory_(this) {} |
| 61 | 62 |
| 62 LocalStorageContextMojo::~LocalStorageContextMojo() {} | 63 LocalStorageContextMojo::~LocalStorageContextMojo() {} |
| 63 | 64 |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 116 CHECK(connector_); | 117 CHECK(connector_); |
| 117 file_service_connection_ = connector_->Connect(file::mojom::kServiceName); | 118 file_service_connection_ = connector_->Connect(file::mojom::kServiceName); |
| 118 connection_state_ = CONNECTION_IN_PROGRESS; | 119 connection_state_ = CONNECTION_IN_PROGRESS; |
| 119 file_service_connection_->AddConnectionCompletedClosure( | 120 file_service_connection_->AddConnectionCompletedClosure( |
| 120 base::Bind(&LocalStorageContextMojo::OnUserServiceConnectionComplete, | 121 base::Bind(&LocalStorageContextMojo::OnUserServiceConnectionComplete, |
| 121 weak_ptr_factory_.GetWeakPtr())); | 122 weak_ptr_factory_.GetWeakPtr())); |
| 122 file_service_connection_->SetConnectionLostClosure( | 123 file_service_connection_->SetConnectionLostClosure( |
| 123 base::Bind(&LocalStorageContextMojo::OnUserServiceConnectionError, | 124 base::Bind(&LocalStorageContextMojo::OnUserServiceConnectionError, |
| 124 weak_ptr_factory_.GetWeakPtr())); | 125 weak_ptr_factory_.GetWeakPtr())); |
| 125 | 126 |
| 126 if (!subdirectory_.empty()) { | 127 InitiateConnection(); |
| 127 // We were given a subdirectory to write to. Get it and use a disk backed | |
| 128 // database. | |
| 129 file_service_connection_->GetInterface(&file_system_); | |
| 130 file_system_->GetSubDirectory( | |
| 131 subdirectory_.AsUTF8Unsafe(), MakeRequest(&directory_), | |
| 132 base::Bind(&LocalStorageContextMojo::OnDirectoryOpened, | |
| 133 weak_ptr_factory_.GetWeakPtr())); | |
| 134 } else { | |
| 135 // We were not given a subdirectory. Use a memory backed database. | |
| 136 file_service_connection_->GetInterface(&leveldb_service_); | |
| 137 leveldb_service_->OpenInMemory( | |
| 138 MakeRequest(&database_), | |
| 139 base::Bind(&LocalStorageContextMojo::OnDatabaseOpened, | |
| 140 weak_ptr_factory_.GetWeakPtr())); | |
| 141 } | |
| 142 } | 128 } |
| 143 | 129 |
| 144 if (connection_state_ == CONNECTION_IN_PROGRESS) { | 130 if (connection_state_ == CONNECTION_IN_PROGRESS) { |
| 145 // Queue this OpenLocalStorage call for when we have a level db pointer. | 131 // Queue this OpenLocalStorage call for when we have a level db pointer. |
| 146 on_database_opened_callbacks_.push_back(std::move(callback)); | 132 on_database_opened_callbacks_.push_back(std::move(callback)); |
| 147 return; | 133 return; |
| 148 } | 134 } |
| 149 | 135 |
| 150 std::move(callback).Run(); | 136 std::move(callback).Run(); |
| 151 } | 137 } |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 197 | 183 |
| 198 void LocalStorageContextMojo::OnUserServiceConnectionComplete() { | 184 void LocalStorageContextMojo::OnUserServiceConnectionComplete() { |
| 199 CHECK_EQ(service_manager::mojom::ConnectResult::SUCCEEDED, | 185 CHECK_EQ(service_manager::mojom::ConnectResult::SUCCEEDED, |
| 200 file_service_connection_->GetResult()); | 186 file_service_connection_->GetResult()); |
| 201 } | 187 } |
| 202 | 188 |
| 203 void LocalStorageContextMojo::OnUserServiceConnectionError() { | 189 void LocalStorageContextMojo::OnUserServiceConnectionError() { |
| 204 CHECK(false); | 190 CHECK(false); |
| 205 } | 191 } |
| 206 | 192 |
| 207 // Part of our asynchronous directory opening called from OpenLocalStorage(). | 193 void LocalStorageContextMojo::InitiateConnection(bool in_memory_only) { |
| 194 DCHECK_EQ(connection_state_, CONNECTION_IN_PROGRESS); | |
| 195 if (!subdirectory_.empty() && !in_memory_only) { | |
| 196 // We were given a subdirectory to write to. Get it and use a disk backed | |
| 197 // database. | |
| 198 file_service_connection_->GetInterface(&file_system_); | |
| 199 file_system_->GetSubDirectory( | |
| 200 subdirectory_.AsUTF8Unsafe(), MakeRequest(&directory_), | |
| 201 base::Bind(&LocalStorageContextMojo::OnDirectoryOpened, | |
| 202 weak_ptr_factory_.GetWeakPtr())); | |
| 203 } else { | |
| 204 // We were not given a subdirectory. Use a memory backed database. | |
| 205 file_service_connection_->GetInterface(&leveldb_service_); | |
| 206 leveldb_service_->OpenInMemory( | |
| 207 MakeRequest(&database_), | |
| 208 base::Bind(&LocalStorageContextMojo::OnDatabaseOpened, | |
| 209 weak_ptr_factory_.GetWeakPtr())); | |
| 210 } | |
| 211 } | |
| 212 | |
| 208 void LocalStorageContextMojo::OnDirectoryOpened( | 213 void LocalStorageContextMojo::OnDirectoryOpened( |
| 209 filesystem::mojom::FileError err) { | 214 filesystem::mojom::FileError err) { |
| 210 if (err != filesystem::mojom::FileError::OK) { | 215 if (err != filesystem::mojom::FileError::OK) { |
| 211 // We failed to open the directory; continue with startup so that we create | 216 // We failed to open the directory; continue with startup so that we create |
| 212 // the |level_db_wrappers_|. | 217 // the |level_db_wrappers_|. |
| 213 OnDatabaseOpened(leveldb::mojom::DatabaseError::IO_ERROR); | 218 OnDatabaseOpened(leveldb::mojom::DatabaseError::IO_ERROR); |
| 214 return; | 219 return; |
| 215 } | 220 } |
| 216 | 221 |
| 217 // Now that we have a directory, connect to the LevelDB service and get our | 222 // Now that we have a directory, connect to the LevelDB service and get our |
| 218 // database. | 223 // database. |
| 219 file_service_connection_->GetInterface(&leveldb_service_); | 224 file_service_connection_->GetInterface(&leveldb_service_); |
| 220 | 225 |
| 226 // We might still need to directory, so create a clone. | |
| 227 filesystem::mojom::DirectoryPtr directory_clone; | |
| 228 directory_->Clone(MakeRequest(&directory_clone)); | |
| 229 | |
| 221 auto options = leveldb::mojom::OpenOptions::New(); | 230 auto options = leveldb::mojom::OpenOptions::New(); |
| 222 options->create_if_missing = true; | 231 options->create_if_missing = true; |
| 223 leveldb_service_->OpenWithOptions( | 232 leveldb_service_->OpenWithOptions( |
| 224 std::move(options), std::move(directory_), "leveldb", | 233 std::move(options), std::move(directory_clone), "leveldb", |
| 225 MakeRequest(&database_), | 234 MakeRequest(&database_), |
| 226 base::Bind(&LocalStorageContextMojo::OnDatabaseOpened, | 235 base::Bind(&LocalStorageContextMojo::OnDatabaseOpened, |
| 227 weak_ptr_factory_.GetWeakPtr())); | 236 weak_ptr_factory_.GetWeakPtr())); |
| 228 } | 237 } |
| 229 | 238 |
| 230 void LocalStorageContextMojo::OnDatabaseOpened( | 239 void LocalStorageContextMojo::OnDatabaseOpened( |
| 231 leveldb::mojom::DatabaseError status) { | 240 leveldb::mojom::DatabaseError status) { |
| 232 if (status != leveldb::mojom::DatabaseError::OK) { | 241 if (status != leveldb::mojom::DatabaseError::OK) { |
| 233 // If we failed to open the database, reset the service object so we pass | 242 // If we failed to open the database, reset the service object so we pass |
| 234 // null pointers to our wrappers. | 243 // null pointers to our wrappers. |
| 235 database_.reset(); | 244 database_.reset(); |
| 236 leveldb_service_.reset(); | |
| 237 } | 245 } |
| 238 | 246 |
| 239 // We no longer need the file service; we've either transferred |directory_| | |
| 240 // to the leveldb service, or we got a file error and no more is possible. | |
| 241 directory_.reset(); | |
| 242 file_system_.reset(); | |
| 243 | |
| 244 // Verify DB schema version. | 247 // Verify DB schema version. |
| 245 if (database_) { | 248 if (database_) { |
| 246 database_->Get(leveldb::StdStringToUint8Vector(kVersionKey), | 249 database_->Get(leveldb::StdStringToUint8Vector(kVersionKey), |
| 247 base::Bind(&LocalStorageContextMojo::OnGotDatabaseVersion, | 250 base::Bind(&LocalStorageContextMojo::OnGotDatabaseVersion, |
| 248 weak_ptr_factory_.GetWeakPtr())); | 251 weak_ptr_factory_.GetWeakPtr())); |
| 249 return; | 252 return; |
| 250 } | 253 } |
| 251 | 254 |
| 252 OnGotDatabaseVersion(leveldb::mojom::DatabaseError::IO_ERROR, | 255 OnGotDatabaseVersion(leveldb::mojom::DatabaseError::IO_ERROR, |
| 253 std::vector<uint8_t>()); | 256 std::vector<uint8_t>()); |
| 254 } | 257 } |
| 255 | 258 |
| 256 void LocalStorageContextMojo::OnGotDatabaseVersion( | 259 void LocalStorageContextMojo::OnGotDatabaseVersion( |
| 257 leveldb::mojom::DatabaseError status, | 260 leveldb::mojom::DatabaseError status, |
| 258 const std::vector<uint8_t>& value) { | 261 const std::vector<uint8_t>& value) { |
| 259 if (status == leveldb::mojom::DatabaseError::NOT_FOUND) { | 262 if (status == leveldb::mojom::DatabaseError::NOT_FOUND) { |
| 260 // New database, nothing more to do. Current version will get written | 263 // New database, nothing more to do. Current version will get written |
| 261 // when first data is committed. | 264 // when first data is committed. |
| 262 } else if (status == leveldb::mojom::DatabaseError::OK) { | 265 } else if (status == leveldb::mojom::DatabaseError::OK) { |
| 263 // Existing database, check if version number matches current schema | 266 // Existing database, check if version number matches current schema |
| 264 // version. | 267 // version. |
| 265 int64_t db_version; | 268 int64_t db_version; |
| 266 if (!base::StringToInt64(leveldb::Uint8VectorToStdString(value), | 269 if (!base::StringToInt64(leveldb::Uint8VectorToStdString(value), |
| 267 &db_version) || | 270 &db_version) || |
| 268 db_version < kMinSchemaVersion || db_version > kCurrentSchemaVersion) { | 271 db_version < kMinSchemaVersion || db_version > kCurrentSchemaVersion) { |
| 269 // TODO(mek): delete and recreate database, rather than failing outright. | 272 DeleteAndRecreateDatabase(); |
| 270 database_ = nullptr; | 273 return; |
| 271 } | 274 } |
| 272 | 275 |
| 273 database_initialized_ = true; | 276 database_initialized_ = true; |
| 274 } else { | 277 } else { |
| 275 // Other read error. Possibly database corruption. | 278 // Other read error. Possibly database corruption. |
| 276 // TODO(mek): delete and recreate database, rather than failing outright. | 279 DeleteAndRecreateDatabase(); |
| 277 database_ = nullptr; | 280 return; |
| 278 } | 281 } |
| 279 | 282 |
| 283 OnConnectionFinished(); | |
| 284 } | |
| 285 | |
| 286 void LocalStorageContextMojo::OnConnectionFinished() { | |
| 287 DCHECK_EQ(connection_state_, CONNECTION_IN_PROGRESS); | |
| 288 | |
| 289 // We no longer need the file service; we've either transferred |directory_| | |
| 290 // to the leveldb service, or we got a file error and no more is possible. | |
| 291 directory_.reset(); | |
| 292 file_system_.reset(); | |
| 293 if (!database_) | |
| 294 leveldb_service_.reset(); | |
| 295 | |
| 280 // |database_| should be known to either be valid or invalid by now. Run our | 296 // |database_| should be known to either be valid or invalid by now. Run our |
| 281 // delayed bindings. | 297 // delayed bindings. |
| 282 connection_state_ = CONNECTION_FINISHED; | 298 connection_state_ = CONNECTION_FINISHED; |
| 283 for (size_t i = 0; i < on_database_opened_callbacks_.size(); ++i) | 299 for (size_t i = 0; i < on_database_opened_callbacks_.size(); ++i) |
| 284 std::move(on_database_opened_callbacks_[i]).Run(); | 300 std::move(on_database_opened_callbacks_[i]).Run(); |
| 285 on_database_opened_callbacks_.clear(); | 301 on_database_opened_callbacks_.clear(); |
| 286 } | 302 } |
| 287 | 303 |
| 304 void LocalStorageContextMojo::DeleteAndRecreateDatabase() { | |
| 305 // For now don't support deletion and recreation when already connected. | |
| 306 DCHECK_EQ(connection_state_, CONNECTION_IN_PROGRESS); | |
| 307 | |
| 308 bool recreate_in_memory = false; | |
| 309 | |
| 310 // If tried to recreate database on disk already, try again but this time | |
| 311 // in memory. | |
| 312 if (tried_to_recreate_ && !subdirectory_.empty()) { | |
| 313 recreate_in_memory = true; | |
| 314 } else if (tried_to_recreate_) { | |
| 315 // Give up completely, run without any database. | |
| 316 database_ = nullptr; | |
| 317 OnConnectionFinished(); | |
| 318 return; | |
| 319 } | |
| 320 | |
| 321 tried_to_recreate_ = true; | |
| 322 | |
| 323 // Unit tests might not have a file_service_connection_, in which case there | |
| 324 // is nothing to rety. | |
| 325 if (!file_service_connection_) { | |
| 326 database_ = nullptr; | |
| 327 OnConnectionFinished(); | |
| 328 return; | |
| 329 } | |
| 330 | |
| 331 // Close and destroy database, and try again. | |
| 332 // TODO(mek): Somehow ensure closing is processed before Destroy is called. | |
|
Marijn Kruisselbrink
2017/01/11 19:10:58
This TODO is somewhat problematic, since I'm not s
jam
2017/01/12 17:24:42
If Destroy is on LevelDBInterface, won't there be
Marijn Kruisselbrink
2017/01/13 19:25:11
I suppose so, although it seemed less likely that
jam
2017/01/14 00:17:55
JavaScript support is being worked on :)
Associat
| |
| 333 database_ = nullptr; | |
| 334 if (directory_.is_bound()) { | |
| 335 leveldb_service_->Destroy( | |
| 336 std::move(directory_), "leveldb", | |
| 337 base::Bind(&LocalStorageContextMojo::OnDBDestroyed, | |
| 338 weak_ptr_factory_.GetWeakPtr(), recreate_in_memory)); | |
| 339 } else { | |
| 340 // No directory, so nothing to destroy. Retrying to recreate will probably | |
| 341 // fail, but try anyway. | |
| 342 InitiateConnection(recreate_in_memory); | |
| 343 } | |
| 344 } | |
| 345 | |
| 346 void LocalStorageContextMojo::OnDBDestroyed( | |
| 347 bool recreate_in_memory, | |
| 348 leveldb::mojom::DatabaseError status) { | |
| 349 // We're essentially ignoring the status here. Even if destroying failed we | |
| 350 // still want to go ahead and try to recreate. | |
| 351 InitiateConnection(recreate_in_memory); | |
| 352 } | |
| 353 | |
| 288 // The (possibly delayed) implementation of OpenLocalStorage(). Can be called | 354 // The (possibly delayed) implementation of OpenLocalStorage(). Can be called |
| 289 // directly from that function, or through |on_database_open_callbacks_|. | 355 // directly from that function, or through |on_database_open_callbacks_|. |
| 290 void LocalStorageContextMojo::BindLocalStorage( | 356 void LocalStorageContextMojo::BindLocalStorage( |
| 291 const url::Origin& origin, | 357 const url::Origin& origin, |
| 292 mojom::LevelDBWrapperRequest request) { | 358 mojom::LevelDBWrapperRequest request) { |
| 293 GetOrCreateDBWrapper(origin)->Bind(std::move(request)); | 359 GetOrCreateDBWrapper(origin)->Bind(std::move(request)); |
| 294 } | 360 } |
| 295 | 361 |
| 296 LevelDBWrapperImpl* LocalStorageContextMojo::GetOrCreateDBWrapper( | 362 LevelDBWrapperImpl* LocalStorageContextMojo::GetOrCreateDBWrapper( |
| 297 const url::Origin& origin) { | 363 const url::Origin& origin) { |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 364 for (const auto& info : usage) { | 430 for (const auto& info : usage) { |
| 365 url::Origin origin_candidate(info.origin); | 431 url::Origin origin_candidate(info.origin); |
| 366 if (!origin_candidate.IsSameOriginWith(origin) && | 432 if (!origin_candidate.IsSameOriginWith(origin) && |
| 367 origin_candidate.IsSamePhysicalOriginWith(origin)) | 433 origin_candidate.IsSamePhysicalOriginWith(origin)) |
| 368 DeleteStorage(origin_candidate); | 434 DeleteStorage(origin_candidate); |
| 369 } | 435 } |
| 370 DeleteStorage(origin); | 436 DeleteStorage(origin); |
| 371 } | 437 } |
| 372 | 438 |
| 373 } // namespace content | 439 } // namespace content |
| OLD | NEW |