| 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 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 95 GetStorageUsage(base::BindOnce( | 96 GetStorageUsage(base::BindOnce( |
| 96 &LocalStorageContextMojo::OnGotStorageUsageForDeletePhysicalOrigin, | 97 &LocalStorageContextMojo::OnGotStorageUsageForDeletePhysicalOrigin, |
| 97 weak_ptr_factory_.GetWeakPtr(), origin)); | 98 weak_ptr_factory_.GetWeakPtr(), origin)); |
| 98 } | 99 } |
| 99 | 100 |
| 100 void LocalStorageContextMojo::Flush() { | 101 void LocalStorageContextMojo::Flush() { |
| 101 for (const auto& it : level_db_wrappers_) | 102 for (const auto& it : level_db_wrappers_) |
| 102 it.second->ScheduleImmediateCommit(); | 103 it.second->ScheduleImmediateCommit(); |
| 103 } | 104 } |
| 104 | 105 |
| 105 void LocalStorageContextMojo::SetDatabaseForTesting( | 106 leveldb::mojom::LevelDBDatabaseAssociatedRequest |
| 106 leveldb::mojom::LevelDBDatabasePtr database) { | 107 LocalStorageContextMojo::DatabaseRequestForTesting() { |
| 107 DCHECK_EQ(connection_state_, NO_CONNECTION); | 108 DCHECK_EQ(connection_state_, NO_CONNECTION); |
| 108 connection_state_ = CONNECTION_IN_PROGRESS; | 109 connection_state_ = CONNECTION_IN_PROGRESS; |
| 109 database_ = std::move(database); | 110 leveldb::mojom::LevelDBDatabaseAssociatedRequest request = |
| 111 MakeRequestForTesting(&database_); |
| 110 OnDatabaseOpened(leveldb::mojom::DatabaseError::OK); | 112 OnDatabaseOpened(leveldb::mojom::DatabaseError::OK); |
| 113 return request; |
| 111 } | 114 } |
| 112 | 115 |
| 113 void LocalStorageContextMojo::RunWhenConnected(base::OnceClosure callback) { | 116 void LocalStorageContextMojo::RunWhenConnected(base::OnceClosure callback) { |
| 114 // If we don't have a filesystem_connection_, we'll need to establish one. | 117 // If we don't have a filesystem_connection_, we'll need to establish one. |
| 115 if (connection_state_ == NO_CONNECTION) { | 118 if (connection_state_ == NO_CONNECTION) { |
| 116 CHECK(connector_); | 119 CHECK(connector_); |
| 117 file_service_connection_ = connector_->Connect(file::mojom::kServiceName); | 120 file_service_connection_ = connector_->Connect(file::mojom::kServiceName); |
| 118 connection_state_ = CONNECTION_IN_PROGRESS; | 121 connection_state_ = CONNECTION_IN_PROGRESS; |
| 119 file_service_connection_->AddConnectionCompletedClosure( | 122 file_service_connection_->AddConnectionCompletedClosure( |
| 120 base::Bind(&LocalStorageContextMojo::OnUserServiceConnectionComplete, | 123 base::Bind(&LocalStorageContextMojo::OnUserServiceConnectionComplete, |
| 121 weak_ptr_factory_.GetWeakPtr())); | 124 weak_ptr_factory_.GetWeakPtr())); |
| 122 file_service_connection_->SetConnectionLostClosure( | 125 file_service_connection_->SetConnectionLostClosure( |
| 123 base::Bind(&LocalStorageContextMojo::OnUserServiceConnectionError, | 126 base::Bind(&LocalStorageContextMojo::OnUserServiceConnectionError, |
| 124 weak_ptr_factory_.GetWeakPtr())); | 127 weak_ptr_factory_.GetWeakPtr())); |
| 125 | 128 |
| 126 if (!subdirectory_.empty()) { | 129 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 } | 130 } |
| 143 | 131 |
| 144 if (connection_state_ == CONNECTION_IN_PROGRESS) { | 132 if (connection_state_ == CONNECTION_IN_PROGRESS) { |
| 145 // Queue this OpenLocalStorage call for when we have a level db pointer. | 133 // Queue this OpenLocalStorage call for when we have a level db pointer. |
| 146 on_database_opened_callbacks_.push_back(std::move(callback)); | 134 on_database_opened_callbacks_.push_back(std::move(callback)); |
| 147 return; | 135 return; |
| 148 } | 136 } |
| 149 | 137 |
| 150 std::move(callback).Run(); | 138 std::move(callback).Run(); |
| 151 } | 139 } |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 197 | 185 |
| 198 void LocalStorageContextMojo::OnUserServiceConnectionComplete() { | 186 void LocalStorageContextMojo::OnUserServiceConnectionComplete() { |
| 199 CHECK_EQ(service_manager::mojom::ConnectResult::SUCCEEDED, | 187 CHECK_EQ(service_manager::mojom::ConnectResult::SUCCEEDED, |
| 200 file_service_connection_->GetResult()); | 188 file_service_connection_->GetResult()); |
| 201 } | 189 } |
| 202 | 190 |
| 203 void LocalStorageContextMojo::OnUserServiceConnectionError() { | 191 void LocalStorageContextMojo::OnUserServiceConnectionError() { |
| 204 CHECK(false); | 192 CHECK(false); |
| 205 } | 193 } |
| 206 | 194 |
| 207 // Part of our asynchronous directory opening called from OpenLocalStorage(). | 195 void LocalStorageContextMojo::InitiateConnection(bool in_memory_only) { |
| 196 DCHECK_EQ(connection_state_, CONNECTION_IN_PROGRESS); |
| 197 if (!subdirectory_.empty() && !in_memory_only) { |
| 198 // We were given a subdirectory to write to. Get it and use a disk backed |
| 199 // database. |
| 200 file_service_connection_->GetInterface(&file_system_); |
| 201 file_system_->GetSubDirectory( |
| 202 subdirectory_.AsUTF8Unsafe(), MakeRequest(&directory_), |
| 203 base::Bind(&LocalStorageContextMojo::OnDirectoryOpened, |
| 204 weak_ptr_factory_.GetWeakPtr())); |
| 205 } else { |
| 206 // We were not given a subdirectory. Use a memory backed database. |
| 207 file_service_connection_->GetInterface(&leveldb_service_); |
| 208 leveldb_service_->OpenInMemory( |
| 209 MakeRequest(&database_, leveldb_service_.associated_group()), |
| 210 base::Bind(&LocalStorageContextMojo::OnDatabaseOpened, |
| 211 weak_ptr_factory_.GetWeakPtr())); |
| 212 } |
| 213 } |
| 214 |
| 208 void LocalStorageContextMojo::OnDirectoryOpened( | 215 void LocalStorageContextMojo::OnDirectoryOpened( |
| 209 filesystem::mojom::FileError err) { | 216 filesystem::mojom::FileError err) { |
| 210 if (err != filesystem::mojom::FileError::OK) { | 217 if (err != filesystem::mojom::FileError::OK) { |
| 211 // We failed to open the directory; continue with startup so that we create | 218 // We failed to open the directory; continue with startup so that we create |
| 212 // the |level_db_wrappers_|. | 219 // the |level_db_wrappers_|. |
| 213 OnDatabaseOpened(leveldb::mojom::DatabaseError::IO_ERROR); | 220 OnDatabaseOpened(leveldb::mojom::DatabaseError::IO_ERROR); |
| 214 return; | 221 return; |
| 215 } | 222 } |
| 216 | 223 |
| 217 // Now that we have a directory, connect to the LevelDB service and get our | 224 // Now that we have a directory, connect to the LevelDB service and get our |
| 218 // database. | 225 // database. |
| 219 file_service_connection_->GetInterface(&leveldb_service_); | 226 file_service_connection_->GetInterface(&leveldb_service_); |
| 220 | 227 |
| 228 // We might still need to use the directory, so create a clone. |
| 229 filesystem::mojom::DirectoryPtr directory_clone; |
| 230 directory_->Clone(MakeRequest(&directory_clone)); |
| 231 |
| 221 auto options = leveldb::mojom::OpenOptions::New(); | 232 auto options = leveldb::mojom::OpenOptions::New(); |
| 222 options->create_if_missing = true; | 233 options->create_if_missing = true; |
| 223 leveldb_service_->OpenWithOptions( | 234 leveldb_service_->OpenWithOptions( |
| 224 std::move(options), std::move(directory_), "leveldb", | 235 std::move(options), std::move(directory_clone), "leveldb", |
| 225 MakeRequest(&database_), | 236 MakeRequest(&database_, leveldb_service_.associated_group()), |
| 226 base::Bind(&LocalStorageContextMojo::OnDatabaseOpened, | 237 base::Bind(&LocalStorageContextMojo::OnDatabaseOpened, |
| 227 weak_ptr_factory_.GetWeakPtr())); | 238 weak_ptr_factory_.GetWeakPtr())); |
| 228 } | 239 } |
| 229 | 240 |
| 230 void LocalStorageContextMojo::OnDatabaseOpened( | 241 void LocalStorageContextMojo::OnDatabaseOpened( |
| 231 leveldb::mojom::DatabaseError status) { | 242 leveldb::mojom::DatabaseError status) { |
| 232 if (status != leveldb::mojom::DatabaseError::OK) { | 243 if (status != leveldb::mojom::DatabaseError::OK) { |
| 233 // If we failed to open the database, reset the service object so we pass | 244 // If we failed to open the database, reset the service object so we pass |
| 234 // null pointers to our wrappers. | 245 // null pointers to our wrappers. |
| 235 database_.reset(); | 246 database_.reset(); |
| 236 leveldb_service_.reset(); | |
| 237 } | 247 } |
| 238 | 248 |
| 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. | 249 // Verify DB schema version. |
| 245 if (database_) { | 250 if (database_) { |
| 246 database_->Get(leveldb::StdStringToUint8Vector(kVersionKey), | 251 database_->Get(leveldb::StdStringToUint8Vector(kVersionKey), |
| 247 base::Bind(&LocalStorageContextMojo::OnGotDatabaseVersion, | 252 base::Bind(&LocalStorageContextMojo::OnGotDatabaseVersion, |
| 248 weak_ptr_factory_.GetWeakPtr())); | 253 weak_ptr_factory_.GetWeakPtr())); |
| 249 return; | 254 return; |
| 250 } | 255 } |
| 251 | 256 |
| 252 OnGotDatabaseVersion(leveldb::mojom::DatabaseError::IO_ERROR, | 257 OnGotDatabaseVersion(leveldb::mojom::DatabaseError::IO_ERROR, |
| 253 std::vector<uint8_t>()); | 258 std::vector<uint8_t>()); |
| 254 } | 259 } |
| 255 | 260 |
| 256 void LocalStorageContextMojo::OnGotDatabaseVersion( | 261 void LocalStorageContextMojo::OnGotDatabaseVersion( |
| 257 leveldb::mojom::DatabaseError status, | 262 leveldb::mojom::DatabaseError status, |
| 258 const std::vector<uint8_t>& value) { | 263 const std::vector<uint8_t>& value) { |
| 259 if (status == leveldb::mojom::DatabaseError::NOT_FOUND) { | 264 if (status == leveldb::mojom::DatabaseError::NOT_FOUND) { |
| 260 // New database, nothing more to do. Current version will get written | 265 // New database, nothing more to do. Current version will get written |
| 261 // when first data is committed. | 266 // when first data is committed. |
| 262 } else if (status == leveldb::mojom::DatabaseError::OK) { | 267 } else if (status == leveldb::mojom::DatabaseError::OK) { |
| 263 // Existing database, check if version number matches current schema | 268 // Existing database, check if version number matches current schema |
| 264 // version. | 269 // version. |
| 265 int64_t db_version; | 270 int64_t db_version; |
| 266 if (!base::StringToInt64(leveldb::Uint8VectorToStdString(value), | 271 if (!base::StringToInt64(leveldb::Uint8VectorToStdString(value), |
| 267 &db_version) || | 272 &db_version) || |
| 268 db_version < kMinSchemaVersion || db_version > kCurrentSchemaVersion) { | 273 db_version < kMinSchemaVersion || db_version > kCurrentSchemaVersion) { |
| 269 // TODO(mek): delete and recreate database, rather than failing outright. | 274 DeleteAndRecreateDatabase(); |
| 270 database_ = nullptr; | 275 return; |
| 271 } | 276 } |
| 272 | 277 |
| 273 database_initialized_ = true; | 278 database_initialized_ = true; |
| 274 } else { | 279 } else { |
| 275 // Other read error. Possibly database corruption. | 280 // Other read error. Possibly database corruption. |
| 276 // TODO(mek): delete and recreate database, rather than failing outright. | 281 DeleteAndRecreateDatabase(); |
| 277 database_ = nullptr; | 282 return; |
| 278 } | 283 } |
| 279 | 284 |
| 285 OnConnectionFinished(); |
| 286 } |
| 287 |
| 288 void LocalStorageContextMojo::OnConnectionFinished() { |
| 289 DCHECK_EQ(connection_state_, CONNECTION_IN_PROGRESS); |
| 290 |
| 291 // We no longer need the file service; we've either transferred |directory_| |
| 292 // to the leveldb service, or we got a file error and no more is possible. |
| 293 directory_.reset(); |
| 294 file_system_.reset(); |
| 295 if (!database_) |
| 296 leveldb_service_.reset(); |
| 297 |
| 280 // |database_| should be known to either be valid or invalid by now. Run our | 298 // |database_| should be known to either be valid or invalid by now. Run our |
| 281 // delayed bindings. | 299 // delayed bindings. |
| 282 connection_state_ = CONNECTION_FINISHED; | 300 connection_state_ = CONNECTION_FINISHED; |
| 283 for (size_t i = 0; i < on_database_opened_callbacks_.size(); ++i) | 301 for (size_t i = 0; i < on_database_opened_callbacks_.size(); ++i) |
| 284 std::move(on_database_opened_callbacks_[i]).Run(); | 302 std::move(on_database_opened_callbacks_[i]).Run(); |
| 285 on_database_opened_callbacks_.clear(); | 303 on_database_opened_callbacks_.clear(); |
| 286 } | 304 } |
| 287 | 305 |
| 306 void LocalStorageContextMojo::DeleteAndRecreateDatabase() { |
| 307 // For now don't support deletion and recreation when already connected. |
| 308 DCHECK_EQ(connection_state_, CONNECTION_IN_PROGRESS); |
| 309 |
| 310 bool recreate_in_memory = false; |
| 311 |
| 312 // If tried to recreate database on disk already, try again but this time |
| 313 // in memory. |
| 314 if (tried_to_recreate_ && !subdirectory_.empty()) { |
| 315 recreate_in_memory = true; |
| 316 } else if (tried_to_recreate_) { |
| 317 // Give up completely, run without any database. |
| 318 database_ = nullptr; |
| 319 OnConnectionFinished(); |
| 320 return; |
| 321 } |
| 322 |
| 323 tried_to_recreate_ = true; |
| 324 |
| 325 // Unit tests might not have a file_service_connection_, in which case there |
| 326 // is nothing to retry. |
| 327 if (!file_service_connection_) { |
| 328 database_ = nullptr; |
| 329 OnConnectionFinished(); |
| 330 return; |
| 331 } |
| 332 |
| 333 // Close and destroy database, and try again. |
| 334 database_ = nullptr; |
| 335 if (directory_.is_bound()) { |
| 336 leveldb_service_->Destroy( |
| 337 std::move(directory_), "leveldb", |
| 338 base::Bind(&LocalStorageContextMojo::OnDBDestroyed, |
| 339 weak_ptr_factory_.GetWeakPtr(), recreate_in_memory)); |
| 340 } else { |
| 341 // No directory, so nothing to destroy. Retrying to recreate will probably |
| 342 // fail, but try anyway. |
| 343 InitiateConnection(recreate_in_memory); |
| 344 } |
| 345 } |
| 346 |
| 347 void LocalStorageContextMojo::OnDBDestroyed( |
| 348 bool recreate_in_memory, |
| 349 leveldb::mojom::DatabaseError status) { |
| 350 // We're essentially ignoring the status here. Even if destroying failed we |
| 351 // still want to go ahead and try to recreate. |
| 352 InitiateConnection(recreate_in_memory); |
| 353 } |
| 354 |
| 288 // The (possibly delayed) implementation of OpenLocalStorage(). Can be called | 355 // The (possibly delayed) implementation of OpenLocalStorage(). Can be called |
| 289 // directly from that function, or through |on_database_open_callbacks_|. | 356 // directly from that function, or through |on_database_open_callbacks_|. |
| 290 void LocalStorageContextMojo::BindLocalStorage( | 357 void LocalStorageContextMojo::BindLocalStorage( |
| 291 const url::Origin& origin, | 358 const url::Origin& origin, |
| 292 mojom::LevelDBWrapperRequest request) { | 359 mojom::LevelDBWrapperRequest request) { |
| 293 GetOrCreateDBWrapper(origin)->Bind(std::move(request)); | 360 GetOrCreateDBWrapper(origin)->Bind(std::move(request)); |
| 294 } | 361 } |
| 295 | 362 |
| 296 LevelDBWrapperImpl* LocalStorageContextMojo::GetOrCreateDBWrapper( | 363 LevelDBWrapperImpl* LocalStorageContextMojo::GetOrCreateDBWrapper( |
| 297 const url::Origin& origin) { | 364 const url::Origin& origin) { |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 364 for (const auto& info : usage) { | 431 for (const auto& info : usage) { |
| 365 url::Origin origin_candidate(info.origin); | 432 url::Origin origin_candidate(info.origin); |
| 366 if (!origin_candidate.IsSameOriginWith(origin) && | 433 if (!origin_candidate.IsSameOriginWith(origin) && |
| 367 origin_candidate.IsSamePhysicalOriginWith(origin)) | 434 origin_candidate.IsSamePhysicalOriginWith(origin)) |
| 368 DeleteStorage(origin_candidate); | 435 DeleteStorage(origin_candidate); |
| 369 } | 436 } |
| 370 DeleteStorage(origin); | 437 DeleteStorage(origin); |
| 371 } | 438 } |
| 372 | 439 |
| 373 } // namespace content | 440 } // namespace content |
| OLD | NEW |