Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(208)

Side by Side Diff: content/browser/dom_storage/local_storage_context_mojo.cc

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

Powered by Google App Engine
This is Rietveld 408576698