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" |
| 11 #include "content/browser/dom_storage/local_storage_database.pb.h" |
10 #include "content/browser/leveldb_wrapper_impl.h" | 12 #include "content/browser/leveldb_wrapper_impl.h" |
11 #include "content/common/dom_storage/dom_storage_types.h" | 13 #include "content/common/dom_storage/dom_storage_types.h" |
| 14 #include "content/public/browser/local_storage_usage_info.h" |
12 #include "services/file/public/interfaces/constants.mojom.h" | 15 #include "services/file/public/interfaces/constants.mojom.h" |
13 #include "services/service_manager/public/cpp/connection.h" | 16 #include "services/service_manager/public/cpp/connection.h" |
14 #include "services/service_manager/public/cpp/connector.h" | 17 #include "services/service_manager/public/cpp/connector.h" |
15 | 18 |
16 namespace content { | 19 namespace content { |
17 | 20 |
18 // LevelDB database schema | 21 // LevelDB database schema |
19 // ======================= | 22 // ======================= |
20 // | 23 // |
21 // Version 1 (in sorted order): | 24 // Version 1 (in sorted order): |
22 // key: "VERSION" | 25 // key: "VERSION" |
23 // value: "1" | 26 // value: "1" |
24 // | 27 // |
| 28 // key: "META:" + <url::Origin 'origin'> |
| 29 // value: <LocalStorageOriginMetaData serialized as a string> |
| 30 // |
25 // key: "_" + <url::Origin> 'origin'> + '\x00' + <script controlled key> | 31 // key: "_" + <url::Origin> 'origin'> + '\x00' + <script controlled key> |
26 // value: <script controlled value> | 32 // value: <script controlled value> |
27 | 33 |
28 namespace { | 34 namespace { |
| 35 |
29 const char kVersionKey[] = "VERSION"; | 36 const char kVersionKey[] = "VERSION"; |
30 const char kOriginSeparator = '\x00'; | 37 const char kOriginSeparator = '\x00'; |
31 const char kDataPrefix[] = "_"; | 38 const char kDataPrefix[] = "_"; |
| 39 const uint8_t kMetaPrefix[] = {'M', 'E', 'T', 'A', ':'}; |
32 const int64_t kMinSchemaVersion = 1; | 40 const int64_t kMinSchemaVersion = 1; |
33 const int64_t kCurrentSchemaVersion = 1; | 41 const int64_t kCurrentSchemaVersion = 1; |
| 42 |
| 43 std::vector<uint8_t> CreateMetaDataKey(const url::Origin& origin) { |
| 44 auto serialized_origin = leveldb::StdStringToUint8Vector(origin.Serialize()); |
| 45 std::vector<uint8_t> key; |
| 46 key.reserve(arraysize(kMetaPrefix) + serialized_origin.size()); |
| 47 key.insert(key.end(), kMetaPrefix, kMetaPrefix + arraysize(kMetaPrefix)); |
| 48 key.insert(key.end(), serialized_origin.begin(), serialized_origin.end()); |
| 49 return key; |
| 50 } |
34 } | 51 } |
35 | 52 |
36 LocalStorageContextMojo::LocalStorageContextMojo( | 53 LocalStorageContextMojo::LocalStorageContextMojo( |
37 service_manager::Connector* connector, | 54 service_manager::Connector* connector, |
38 const base::FilePath& subdirectory) | 55 const base::FilePath& subdirectory) |
39 : connector_(connector), | 56 : connector_(connector), |
40 subdirectory_(subdirectory), | 57 subdirectory_(subdirectory), |
41 weak_ptr_factory_(this) {} | 58 weak_ptr_factory_(this) {} |
42 | 59 |
43 LocalStorageContextMojo::~LocalStorageContextMojo() {} | 60 LocalStorageContextMojo::~LocalStorageContextMojo() {} |
44 | 61 |
45 void LocalStorageContextMojo::OpenLocalStorage( | 62 void LocalStorageContextMojo::OpenLocalStorage( |
46 const url::Origin& origin, | 63 const url::Origin& origin, |
47 mojom::LevelDBWrapperRequest request) { | 64 mojom::LevelDBWrapperRequest request) { |
| 65 RunWhenConnected(base::BindOnce(&LocalStorageContextMojo::BindLocalStorage, |
| 66 weak_ptr_factory_.GetWeakPtr(), origin, |
| 67 std::move(request))); |
| 68 } |
| 69 |
| 70 void LocalStorageContextMojo::GetStorageUsage( |
| 71 GetStorageUsageCallback callback) { |
| 72 RunWhenConnected( |
| 73 base::BindOnce(&LocalStorageContextMojo::RetrieveStorageUsage, |
| 74 weak_ptr_factory_.GetWeakPtr(), std::move(callback))); |
| 75 } |
| 76 |
| 77 void LocalStorageContextMojo::SetDatabaseForTesting( |
| 78 leveldb::mojom::LevelDBDatabasePtr database) { |
| 79 DCHECK_EQ(connection_state_, NO_CONNECTION); |
| 80 connection_state_ = CONNECTION_IN_PROGRESS; |
| 81 database_ = std::move(database); |
| 82 OnDatabaseOpened(leveldb::mojom::DatabaseError::OK); |
| 83 } |
| 84 |
| 85 void LocalStorageContextMojo::RunWhenConnected(base::OnceClosure callback) { |
48 // If we don't have a filesystem_connection_, we'll need to establish one. | 86 // If we don't have a filesystem_connection_, we'll need to establish one. |
49 if (connection_state_ == NO_CONNECTION) { | 87 if (connection_state_ == NO_CONNECTION) { |
50 CHECK(connector_); | 88 CHECK(connector_); |
51 file_service_connection_ = connector_->Connect(file::mojom::kServiceName); | 89 file_service_connection_ = connector_->Connect(file::mojom::kServiceName); |
52 connection_state_ = CONNECTION_IN_PROGRESS; | 90 connection_state_ = CONNECTION_IN_PROGRESS; |
53 file_service_connection_->AddConnectionCompletedClosure( | 91 file_service_connection_->AddConnectionCompletedClosure( |
54 base::Bind(&LocalStorageContextMojo::OnUserServiceConnectionComplete, | 92 base::Bind(&LocalStorageContextMojo::OnUserServiceConnectionComplete, |
55 weak_ptr_factory_.GetWeakPtr())); | 93 weak_ptr_factory_.GetWeakPtr())); |
56 file_service_connection_->SetConnectionLostClosure( | 94 file_service_connection_->SetConnectionLostClosure( |
57 base::Bind(&LocalStorageContextMojo::OnUserServiceConnectionError, | 95 base::Bind(&LocalStorageContextMojo::OnUserServiceConnectionError, |
(...skipping 12 matching lines...) Expand all Loading... |
70 file_service_connection_->GetInterface(&leveldb_service_); | 108 file_service_connection_->GetInterface(&leveldb_service_); |
71 leveldb_service_->OpenInMemory( | 109 leveldb_service_->OpenInMemory( |
72 MakeRequest(&database_), | 110 MakeRequest(&database_), |
73 base::Bind(&LocalStorageContextMojo::OnDatabaseOpened, | 111 base::Bind(&LocalStorageContextMojo::OnDatabaseOpened, |
74 weak_ptr_factory_.GetWeakPtr())); | 112 weak_ptr_factory_.GetWeakPtr())); |
75 } | 113 } |
76 } | 114 } |
77 | 115 |
78 if (connection_state_ == CONNECTION_IN_PROGRESS) { | 116 if (connection_state_ == CONNECTION_IN_PROGRESS) { |
79 // Queue this OpenLocalStorage call for when we have a level db pointer. | 117 // Queue this OpenLocalStorage call for when we have a level db pointer. |
80 on_database_opened_callbacks_.push_back(base::BindOnce( | 118 on_database_opened_callbacks_.push_back(std::move(callback)); |
81 &LocalStorageContextMojo::BindLocalStorage, | |
82 weak_ptr_factory_.GetWeakPtr(), origin, std::move(request))); | |
83 return; | 119 return; |
84 } | 120 } |
85 | 121 |
86 BindLocalStorage(origin, std::move(request)); | 122 std::move(callback).Run(); |
87 } | |
88 | |
89 void LocalStorageContextMojo::SetDatabaseForTesting( | |
90 leveldb::mojom::LevelDBDatabasePtr database) { | |
91 DCHECK_EQ(connection_state_, NO_CONNECTION); | |
92 connection_state_ = CONNECTION_IN_PROGRESS; | |
93 database_ = std::move(database); | |
94 OnDatabaseOpened(leveldb::mojom::DatabaseError::OK); | |
95 } | 123 } |
96 | 124 |
97 void LocalStorageContextMojo::OnLevelDBWrapperHasNoBindings( | 125 void LocalStorageContextMojo::OnLevelDBWrapperHasNoBindings( |
98 const url::Origin& origin) { | 126 const url::Origin& origin) { |
99 DCHECK(level_db_wrappers_.find(origin) != level_db_wrappers_.end()); | 127 DCHECK(level_db_wrappers_.find(origin) != level_db_wrappers_.end()); |
100 level_db_wrappers_.erase(origin); | 128 level_db_wrappers_.erase(origin); |
101 } | 129 } |
102 | 130 |
103 std::vector<leveldb::mojom::BatchedOperationPtr> | 131 std::vector<leveldb::mojom::BatchedOperationPtr> |
104 LocalStorageContextMojo::OnLevelDBWrapperPrepareToCommit() { | 132 LocalStorageContextMojo::OnLevelDBWrapperPrepareToCommit( |
| 133 const url::Origin& origin, |
| 134 const LevelDBWrapperImpl& wrapper) { |
| 135 // |wrapper| might not exist in |level_db_wrappers_| anymore at this point, as |
| 136 // it is possible this commit was triggered by destruction. |
| 137 |
105 std::vector<leveldb::mojom::BatchedOperationPtr> operations; | 138 std::vector<leveldb::mojom::BatchedOperationPtr> operations; |
106 | 139 |
107 // Write schema version if not already done so before. | 140 // Write schema version if not already done so before. |
108 if (!database_initialized_) { | 141 if (!database_initialized_) { |
109 leveldb::mojom::BatchedOperationPtr item = | 142 leveldb::mojom::BatchedOperationPtr item = |
110 leveldb::mojom::BatchedOperation::New(); | 143 leveldb::mojom::BatchedOperation::New(); |
| 144 item->type = leveldb::mojom::BatchOperationType::PUT_KEY; |
111 item->key = leveldb::StdStringToUint8Vector(kVersionKey); | 145 item->key = leveldb::StdStringToUint8Vector(kVersionKey); |
112 item->value = leveldb::StdStringToUint8Vector( | 146 item->value = leveldb::StdStringToUint8Vector( |
113 base::Int64ToString(kCurrentSchemaVersion)); | 147 base::Int64ToString(kCurrentSchemaVersion)); |
114 operations.push_back(std::move(item)); | 148 operations.push_back(std::move(item)); |
115 database_initialized_ = true; | 149 database_initialized_ = true; |
116 } | 150 } |
117 | 151 |
| 152 leveldb::mojom::BatchedOperationPtr item = |
| 153 leveldb::mojom::BatchedOperation::New(); |
| 154 item->key = CreateMetaDataKey(origin); |
| 155 if (wrapper.bytes_used() == 0) { |
| 156 item->type = leveldb::mojom::BatchOperationType::DELETE_KEY; |
| 157 } else { |
| 158 item->type = leveldb::mojom::BatchOperationType::PUT_KEY; |
| 159 LocalStorageOriginMetaData data; |
| 160 data.set_last_modified(base::Time::Now().ToInternalValue()); |
| 161 data.set_size_bytes(wrapper.bytes_used()); |
| 162 item->value = leveldb::StdStringToUint8Vector(data.SerializeAsString()); |
| 163 } |
| 164 operations.push_back(std::move(item)); |
| 165 |
118 return operations; | 166 return operations; |
119 } | 167 } |
120 | 168 |
121 void LocalStorageContextMojo::OnUserServiceConnectionComplete() { | 169 void LocalStorageContextMojo::OnUserServiceConnectionComplete() { |
122 CHECK_EQ(service_manager::mojom::ConnectResult::SUCCEEDED, | 170 CHECK_EQ(service_manager::mojom::ConnectResult::SUCCEEDED, |
123 file_service_connection_->GetResult()); | 171 file_service_connection_->GetResult()); |
124 } | 172 } |
125 | 173 |
126 void LocalStorageContextMojo::OnUserServiceConnectionError() { | 174 void LocalStorageContextMojo::OnUserServiceConnectionError() { |
127 CHECK(false); | 175 CHECK(false); |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
225 auto found = level_db_wrappers_.find(origin); | 273 auto found = level_db_wrappers_.find(origin); |
226 if (found == level_db_wrappers_.end()) { | 274 if (found == level_db_wrappers_.end()) { |
227 level_db_wrappers_[origin] = base::MakeUnique<LevelDBWrapperImpl>( | 275 level_db_wrappers_[origin] = base::MakeUnique<LevelDBWrapperImpl>( |
228 database_.get(), kDataPrefix + origin.Serialize() + kOriginSeparator, | 276 database_.get(), kDataPrefix + origin.Serialize() + kOriginSeparator, |
229 kPerStorageAreaQuota + kPerStorageAreaOverQuotaAllowance, | 277 kPerStorageAreaQuota + kPerStorageAreaOverQuotaAllowance, |
230 base::TimeDelta::FromSeconds(kCommitDefaultDelaySecs), kMaxBytesPerHour, | 278 base::TimeDelta::FromSeconds(kCommitDefaultDelaySecs), kMaxBytesPerHour, |
231 kMaxCommitsPerHour, | 279 kMaxCommitsPerHour, |
232 base::Bind(&LocalStorageContextMojo::OnLevelDBWrapperHasNoBindings, | 280 base::Bind(&LocalStorageContextMojo::OnLevelDBWrapperHasNoBindings, |
233 base::Unretained(this), origin), | 281 base::Unretained(this), origin), |
234 base::Bind(&LocalStorageContextMojo::OnLevelDBWrapperPrepareToCommit, | 282 base::Bind(&LocalStorageContextMojo::OnLevelDBWrapperPrepareToCommit, |
235 base::Unretained(this))); | 283 base::Unretained(this), origin)); |
236 found = level_db_wrappers_.find(origin); | 284 found = level_db_wrappers_.find(origin); |
237 } | 285 } |
238 | 286 |
239 found->second->Bind(std::move(request)); | 287 found->second->Bind(std::move(request)); |
240 } | 288 } |
241 | 289 |
| 290 void LocalStorageContextMojo::RetrieveStorageUsage( |
| 291 GetStorageUsageCallback callback) { |
| 292 database_->GetPrefixed( |
| 293 std::vector<uint8_t>(kMetaPrefix, kMetaPrefix + arraysize(kMetaPrefix)), |
| 294 base::Bind(&LocalStorageContextMojo::OnGotMetaData, |
| 295 weak_ptr_factory_.GetWeakPtr(), base::Passed(&callback))); |
| 296 } |
| 297 |
| 298 void LocalStorageContextMojo::OnGotMetaData( |
| 299 GetStorageUsageCallback callback, |
| 300 leveldb::mojom::DatabaseError status, |
| 301 std::vector<leveldb::mojom::KeyValuePtr> data) { |
| 302 std::vector<LocalStorageUsageInfo> result; |
| 303 for (const auto& row : data) { |
| 304 DCHECK_GT(row->key.size(), arraysize(kMetaPrefix)); |
| 305 LocalStorageUsageInfo info; |
| 306 info.origin = GURL(leveldb::Uint8VectorToStdString(row->key).substr( |
| 307 arraysize(kMetaPrefix))); |
| 308 if (!info.origin.is_valid()) { |
| 309 // TODO(mek): Deal with database corruption. |
| 310 continue; |
| 311 } |
| 312 |
| 313 LocalStorageOriginMetaData data; |
| 314 if (!data.ParseFromArray(row->value.data(), row->value.size())) { |
| 315 // TODO(mek): Deal with database corruption. |
| 316 continue; |
| 317 } |
| 318 info.data_size = data.size_bytes(); |
| 319 info.last_modified = base::Time::FromInternalValue(data.last_modified()); |
| 320 result.push_back(std::move(info)); |
| 321 } |
| 322 std::move(callback).Run(std::move(result)); |
| 323 } |
| 324 |
242 } // namespace content | 325 } // namespace content |
OLD | NEW |