OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 "webkit/quota/usage_tracker.h" | 5 #include "webkit/quota/usage_tracker.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <deque> | 8 #include <deque> |
9 #include <set> | 9 #include <set> |
10 #include <string> | 10 #include <string> |
| 11 #include <vector> |
11 | 12 |
| 13 #include "base/bind.h" |
12 #include "base/message_loop_proxy.h" | 14 #include "base/message_loop_proxy.h" |
13 #include "base/stl_util.h" | 15 #include "base/stl_util.h" |
14 #include "net/base/net_util.h" | 16 #include "net/base/net_util.h" |
15 | 17 |
16 namespace quota { | 18 namespace quota { |
17 | 19 |
18 namespace { | 20 namespace { |
19 bool SortByHost(const GURL& lhs, const GURL& rhs) { | 21 bool SortByHost(const GURL& lhs, const GURL& rhs) { |
20 return net::GetHostOrSpecFromURL(lhs) > net::GetHostOrSpecFromURL(rhs); | 22 return net::GetHostOrSpecFromURL(lhs) > net::GetHostOrSpecFromURL(rhs); |
21 } | 23 } |
22 } | 24 } |
23 | 25 |
24 // A task class for getting the total amount of data used for a collection of | 26 // A task class for getting the total amount of data used for a collection of |
25 // origins. This class is self-destructed. | 27 // origins. This class is self-destructed. |
26 class ClientUsageTracker::GatherUsageTaskBase : public QuotaTask { | 28 class ClientUsageTracker::GatherUsageTaskBase : public QuotaTask { |
27 public: | 29 public: |
28 GatherUsageTaskBase( | 30 GatherUsageTaskBase( |
29 UsageTracker* tracker, | 31 UsageTracker* tracker, |
30 QuotaClient* client) | 32 QuotaClient* client) |
31 : QuotaTask(tracker), | 33 : QuotaTask(tracker), |
32 client_(client), | 34 client_(client), |
33 tracker_(tracker), | 35 tracker_(tracker), |
34 callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { | 36 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { |
35 DCHECK(tracker_); | 37 DCHECK(tracker_); |
36 DCHECK(client_); | 38 DCHECK(client_); |
37 client_tracker_ = tracker_->GetClientTracker(client_->id()); | 39 client_tracker_ = tracker_->GetClientTracker(client_->id()); |
38 DCHECK(client_tracker_); | 40 DCHECK(client_tracker_); |
39 } | 41 } |
40 virtual ~GatherUsageTaskBase() {} | 42 virtual ~GatherUsageTaskBase() {} |
41 | 43 |
42 // Get total usage for the given |origins|. | 44 // Get total usage for the given |origins|. |
43 void GetUsageForOrigins(const std::set<GURL>& origins, StorageType type) { | 45 void GetUsageForOrigins(const std::set<GURL>& origins, StorageType type) { |
44 DCHECK(original_message_loop()->BelongsToCurrentThread()); | 46 DCHECK(original_message_loop()->BelongsToCurrentThread()); |
(...skipping 23 matching lines...) Expand all Loading... |
68 // the completion callback immediately. | 70 // the completion callback immediately. |
69 for (std::vector<GURL>::const_iterator iter = origins_to_gather.begin(); | 71 for (std::vector<GURL>::const_iterator iter = origins_to_gather.begin(); |
70 iter != origins_to_gather.end(); iter++) | 72 iter != origins_to_gather.end(); iter++) |
71 pending_origins_.push_back(*iter); | 73 pending_origins_.push_back(*iter); |
72 | 74 |
73 for (std::vector<GURL>::const_iterator iter = origins_to_gather.begin(); | 75 for (std::vector<GURL>::const_iterator iter = origins_to_gather.begin(); |
74 iter != origins_to_gather.end(); iter++) | 76 iter != origins_to_gather.end(); iter++) |
75 client_->GetOriginUsage( | 77 client_->GetOriginUsage( |
76 *iter, | 78 *iter, |
77 tracker_->type(), | 79 tracker_->type(), |
78 callback_factory_.NewCallback(&GatherUsageTaskBase::DidGetUsage)); | 80 base::Bind(&GatherUsageTaskBase::DidGetUsage, |
| 81 weak_factory_.GetWeakPtr())); |
79 } | 82 } |
80 | 83 |
81 protected: | 84 protected: |
82 virtual void Aborted() OVERRIDE { | 85 virtual void Aborted() OVERRIDE { |
83 DeleteSoon(); | 86 DeleteSoon(); |
84 } | 87 } |
85 | 88 |
86 UsageTracker* tracker() const { return tracker_; } | 89 UsageTracker* tracker() const { return tracker_; } |
87 ClientUsageTracker* client_tracker() const { return client_tracker_; } | 90 ClientUsageTracker* client_tracker() const { return client_tracker_; } |
88 | 91 |
(...skipping 25 matching lines...) Expand all Loading... |
114 CallCompleted(); | 117 CallCompleted(); |
115 DeleteSoon(); | 118 DeleteSoon(); |
116 } | 119 } |
117 } | 120 } |
118 | 121 |
119 QuotaClient* client_; | 122 QuotaClient* client_; |
120 UsageTracker* tracker_; | 123 UsageTracker* tracker_; |
121 ClientUsageTracker* client_tracker_; | 124 ClientUsageTracker* client_tracker_; |
122 std::deque<GURL> pending_origins_; | 125 std::deque<GURL> pending_origins_; |
123 std::map<GURL, int64> origin_usage_map_; | 126 std::map<GURL, int64> origin_usage_map_; |
124 base::ScopedCallbackFactory<GatherUsageTaskBase> callback_factory_; | 127 base::WeakPtrFactory<GatherUsageTaskBase> weak_factory_; |
125 | 128 |
126 DISALLOW_COPY_AND_ASSIGN(GatherUsageTaskBase); | 129 DISALLOW_COPY_AND_ASSIGN(GatherUsageTaskBase); |
127 }; | 130 }; |
128 | 131 |
129 // A task class for getting the total amount of data used for a given storage | 132 // A task class for getting the total amount of data used for a given storage |
130 // type. This class is self-destructed. | 133 // type. This class is self-destructed. |
131 class ClientUsageTracker::GatherGlobalUsageTask | 134 class ClientUsageTracker::GatherGlobalUsageTask |
132 : public GatherUsageTaskBase { | 135 : public GatherUsageTaskBase { |
133 public: | 136 public: |
134 GatherGlobalUsageTask( | 137 GatherGlobalUsageTask( |
135 UsageTracker* tracker, | 138 UsageTracker* tracker, |
136 QuotaClient* client) | 139 QuotaClient* client) |
137 : GatherUsageTaskBase(tracker, client), | 140 : GatherUsageTaskBase(tracker, client), |
138 client_(client), | 141 client_(client), |
139 callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { | 142 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { |
140 DCHECK(tracker); | 143 DCHECK(tracker); |
141 DCHECK(client); | 144 DCHECK(client); |
142 } | 145 } |
143 virtual ~GatherGlobalUsageTask() {} | 146 virtual ~GatherGlobalUsageTask() {} |
144 | 147 |
145 protected: | 148 protected: |
146 virtual void Run() OVERRIDE { | 149 virtual void Run() OVERRIDE { |
147 client_->GetOriginsForType(tracker()->type(), | 150 client_->GetOriginsForType(tracker()->type(), |
148 callback_factory_.NewCallback( | 151 base::Bind(&GatherUsageTaskBase::GetUsageForOrigins, |
149 &GatherUsageTaskBase::GetUsageForOrigins)); | 152 weak_factory_.GetWeakPtr())); |
150 } | 153 } |
151 | 154 |
152 virtual void Completed() OVERRIDE { | 155 virtual void Completed() OVERRIDE { |
153 client_tracker()->GatherGlobalUsageComplete(); | 156 client_tracker()->GatherGlobalUsageComplete(); |
154 } | 157 } |
155 | 158 |
156 private: | 159 private: |
157 QuotaClient* client_; | 160 QuotaClient* client_; |
158 base::ScopedCallbackFactory<GatherUsageTaskBase> callback_factory_; | 161 base::WeakPtrFactory<GatherUsageTaskBase> weak_factory_; |
159 | 162 |
160 DISALLOW_COPY_AND_ASSIGN(GatherGlobalUsageTask); | 163 DISALLOW_COPY_AND_ASSIGN(GatherGlobalUsageTask); |
161 }; | 164 }; |
162 | 165 |
163 // A task class for getting the total amount of data used for a given host. | 166 // A task class for getting the total amount of data used for a given host. |
164 // This class is self-destructed. | 167 // This class is self-destructed. |
165 class ClientUsageTracker::GatherHostUsageTask | 168 class ClientUsageTracker::GatherHostUsageTask |
166 : public GatherUsageTaskBase { | 169 : public GatherUsageTaskBase { |
167 public: | 170 public: |
168 GatherHostUsageTask( | 171 GatherHostUsageTask( |
169 UsageTracker* tracker, | 172 UsageTracker* tracker, |
170 QuotaClient* client, | 173 QuotaClient* client, |
171 const std::string& host) | 174 const std::string& host) |
172 : GatherUsageTaskBase(tracker, client), | 175 : GatherUsageTaskBase(tracker, client), |
173 client_(client), | 176 client_(client), |
174 host_(host), | 177 host_(host), |
175 callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { | 178 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { |
176 DCHECK(client_); | 179 DCHECK(client_); |
177 } | 180 } |
178 virtual ~GatherHostUsageTask() {} | 181 virtual ~GatherHostUsageTask() {} |
179 | 182 |
180 protected: | 183 protected: |
181 virtual void Run() OVERRIDE { | 184 virtual void Run() OVERRIDE { |
182 client_->GetOriginsForHost(tracker()->type(), host_, | 185 client_->GetOriginsForHost(tracker()->type(), host_, |
183 callback_factory_.NewCallback( | 186 base::Bind(&GatherUsageTaskBase::GetUsageForOrigins, |
184 &GatherUsageTaskBase::GetUsageForOrigins)); | 187 weak_factory_.GetWeakPtr())); |
185 } | 188 } |
186 | 189 |
187 virtual void Completed() OVERRIDE { | 190 virtual void Completed() OVERRIDE { |
188 client_tracker()->GatherHostUsageComplete(host_); | 191 client_tracker()->GatherHostUsageComplete(host_); |
189 } | 192 } |
190 | 193 |
191 private: | 194 private: |
192 QuotaClient* client_; | 195 QuotaClient* client_; |
193 std::string host_; | 196 std::string host_; |
194 base::ScopedCallbackFactory<GatherUsageTaskBase> callback_factory_; | 197 base::WeakPtrFactory<GatherUsageTaskBase> weak_factory_; |
195 | 198 |
196 DISALLOW_COPY_AND_ASSIGN(GatherHostUsageTask); | 199 DISALLOW_COPY_AND_ASSIGN(GatherHostUsageTask); |
197 }; | 200 }; |
198 | 201 |
199 // UsageTracker ---------------------------------------------------------- | 202 // UsageTracker ---------------------------------------------------------- |
200 | 203 |
201 UsageTracker::UsageTracker(const QuotaClientList& clients, StorageType type, | 204 UsageTracker::UsageTracker(const QuotaClientList& clients, StorageType type, |
202 SpecialStoragePolicy* special_storage_policy) | 205 SpecialStoragePolicy* special_storage_policy) |
203 : type_(type), | 206 : type_(type), |
204 callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { | 207 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { |
205 for (QuotaClientList::const_iterator iter = clients.begin(); | 208 for (QuotaClientList::const_iterator iter = clients.begin(); |
206 iter != clients.end(); | 209 iter != clients.end(); |
207 ++iter) { | 210 ++iter) { |
208 client_tracker_map_.insert(std::make_pair( | 211 client_tracker_map_.insert(std::make_pair( |
209 (*iter)->id(), | 212 (*iter)->id(), |
210 new ClientUsageTracker(this, *iter, type, special_storage_policy))); | 213 new ClientUsageTracker(this, *iter, type, special_storage_policy))); |
211 } | 214 } |
212 } | 215 } |
213 | 216 |
214 UsageTracker::~UsageTracker() { | 217 UsageTracker::~UsageTracker() { |
215 STLDeleteValues(&client_tracker_map_); | 218 STLDeleteValues(&client_tracker_map_); |
216 } | 219 } |
217 | 220 |
218 ClientUsageTracker* UsageTracker::GetClientTracker(QuotaClient::ID client_id) { | 221 ClientUsageTracker* UsageTracker::GetClientTracker(QuotaClient::ID client_id) { |
219 ClientTrackerMap::iterator found = client_tracker_map_.find(client_id); | 222 ClientTrackerMap::iterator found = client_tracker_map_.find(client_id); |
220 if (found != client_tracker_map_.end()) | 223 if (found != client_tracker_map_.end()) |
221 return found->second; | 224 return found->second; |
222 return NULL; | 225 return NULL; |
223 } | 226 } |
224 | 227 |
225 void UsageTracker::GetGlobalUsage(GlobalUsageCallback* callback) { | 228 void UsageTracker::GetGlobalUsage(const GlobalUsageCallback& callback) { |
226 if (client_tracker_map_.size() == 0) { | 229 if (client_tracker_map_.size() == 0) { |
227 // No clients registered. | 230 // No clients registered. |
228 callback->Run(type_, 0, 0); | 231 callback.Run(type_, 0, 0); |
229 delete callback; | |
230 return; | 232 return; |
231 } | 233 } |
232 if (global_usage_callbacks_.Add(callback)) { | 234 if (global_usage_callbacks_.Add(callback)) { |
233 // This is the first call. Asks each ClientUsageTracker to collect | 235 // This is the first call. Asks each ClientUsageTracker to collect |
234 // usage information. | 236 // usage information. |
235 global_usage_.pending_clients = client_tracker_map_.size(); | 237 global_usage_.pending_clients = client_tracker_map_.size(); |
236 global_usage_.usage = 0; | 238 global_usage_.usage = 0; |
237 global_usage_.unlimited_usage = 0; | 239 global_usage_.unlimited_usage = 0; |
238 for (ClientTrackerMap::iterator iter = client_tracker_map_.begin(); | 240 for (ClientTrackerMap::iterator iter = client_tracker_map_.begin(); |
239 iter != client_tracker_map_.end(); | 241 iter != client_tracker_map_.end(); |
240 ++iter) { | 242 ++iter) { |
241 iter->second->GetGlobalUsage(callback_factory_.NewCallback( | 243 iter->second->GetGlobalUsage( |
242 &UsageTracker::DidGetClientGlobalUsage)); | 244 base::Bind(&UsageTracker::DidGetClientGlobalUsage, |
| 245 weak_factory_.GetWeakPtr())); |
243 } | 246 } |
244 } | 247 } |
245 } | 248 } |
246 | 249 |
247 void UsageTracker::GetHostUsage( | 250 void UsageTracker::GetHostUsage( |
248 const std::string& host, HostUsageCallback* callback) { | 251 const std::string& host, const HostUsageCallback& callback) { |
249 if (client_tracker_map_.size() == 0) { | 252 if (client_tracker_map_.size() == 0) { |
250 // No clients registered. | 253 // No clients registered. |
251 callback->Run(host, type_, 0); | 254 callback.Run(host, type_, 0); |
252 delete callback; | |
253 return; | 255 return; |
254 } | 256 } |
255 if (host_usage_callbacks_.Add(host, callback)) { | 257 if (host_usage_callbacks_.Add(host, callback)) { |
256 // This is the first call for the given host. | 258 // This is the first call for the given host. |
257 DCHECK(outstanding_host_usage_.find(host) == outstanding_host_usage_.end()); | 259 DCHECK(outstanding_host_usage_.find(host) == outstanding_host_usage_.end()); |
258 outstanding_host_usage_[host].pending_clients = client_tracker_map_.size(); | 260 outstanding_host_usage_[host].pending_clients = client_tracker_map_.size(); |
259 for (ClientTrackerMap::iterator iter = client_tracker_map_.begin(); | 261 for (ClientTrackerMap::iterator iter = client_tracker_map_.begin(); |
260 iter != client_tracker_map_.end(); | 262 iter != client_tracker_map_.end(); |
261 ++iter) { | 263 ++iter) { |
262 iter->second->GetHostUsage(host, callback_factory_.NewCallback( | 264 iter->second->GetHostUsage(host, |
263 &UsageTracker::DidGetClientHostUsage)); | 265 base::Bind(&UsageTracker::DidGetClientHostUsage, |
| 266 weak_factory_.GetWeakPtr())); |
264 } | 267 } |
265 } | 268 } |
266 } | 269 } |
267 | 270 |
268 void UsageTracker::UpdateUsageCache( | 271 void UsageTracker::UpdateUsageCache( |
269 QuotaClient::ID client_id, const GURL& origin, int64 delta) { | 272 QuotaClient::ID client_id, const GURL& origin, int64 delta) { |
270 ClientUsageTracker* client_tracker = GetClientTracker(client_id); | 273 ClientUsageTracker* client_tracker = GetClientTracker(client_id); |
271 DCHECK(client_tracker); | 274 DCHECK(client_tracker); |
272 client_tracker->UpdateUsageCache(origin, delta); | 275 client_tracker->UpdateUsageCache(origin, delta); |
273 } | 276 } |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
350 DCHECK(client_); | 353 DCHECK(client_); |
351 if (special_storage_policy_) | 354 if (special_storage_policy_) |
352 special_storage_policy_->AddObserver(this); | 355 special_storage_policy_->AddObserver(this); |
353 } | 356 } |
354 | 357 |
355 ClientUsageTracker::~ClientUsageTracker() { | 358 ClientUsageTracker::~ClientUsageTracker() { |
356 if (special_storage_policy_) | 359 if (special_storage_policy_) |
357 special_storage_policy_->RemoveObserver(this); | 360 special_storage_policy_->RemoveObserver(this); |
358 } | 361 } |
359 | 362 |
360 void ClientUsageTracker::GetGlobalUsage(GlobalUsageCallback* callback) { | 363 void ClientUsageTracker::GetGlobalUsage(const GlobalUsageCallback& callback) { |
361 if (global_usage_retrieved_) { | 364 if (global_usage_retrieved_) { |
362 callback->Run(type_, global_usage_, GetCachedGlobalUnlimitedUsage()); | 365 callback.Run(type_, global_usage_, GetCachedGlobalUnlimitedUsage()); |
363 delete callback; | |
364 return; | 366 return; |
365 } | 367 } |
366 DCHECK(!global_usage_callback_.HasCallbacks()); | 368 DCHECK(!global_usage_callback_.HasCallbacks()); |
367 global_usage_callback_.Add(callback); | 369 global_usage_callback_.Add(callback); |
368 global_usage_task_ = new GatherGlobalUsageTask(tracker_, client_); | 370 global_usage_task_ = new GatherGlobalUsageTask(tracker_, client_); |
369 global_usage_task_->Start(); | 371 global_usage_task_->Start(); |
370 } | 372 } |
371 | 373 |
372 void ClientUsageTracker::GetHostUsage( | 374 void ClientUsageTracker::GetHostUsage( |
373 const std::string& host, HostUsageCallback* callback) { | 375 const std::string& host, const HostUsageCallback& callback) { |
374 HostSet::const_iterator found = cached_hosts_.find(host); | 376 HostSet::const_iterator found = cached_hosts_.find(host); |
375 if (found != cached_hosts_.end()) { | 377 if (found != cached_hosts_.end()) { |
376 // TODO(kinuko): Drop host_usage_map_ cache periodically. | 378 // TODO(kinuko): Drop host_usage_map_ cache periodically. |
377 callback->Run(host, type_, GetCachedHostUsage(host)); | 379 callback.Run(host, type_, GetCachedHostUsage(host)); |
378 delete callback; | |
379 return; | 380 return; |
380 } | 381 } |
381 if (!host_usage_callbacks_.Add(host, callback) || global_usage_task_) | 382 if (!host_usage_callbacks_.Add(host, callback) || global_usage_task_) |
382 return; | 383 return; |
383 GatherHostUsageTask* task = new GatherHostUsageTask(tracker_, client_, host); | 384 GatherHostUsageTask* task = new GatherHostUsageTask(tracker_, client_, host); |
384 host_usage_tasks_[host] = task; | 385 host_usage_tasks_[host] = task; |
385 task->Start(); | 386 task->Start(); |
386 } | 387 } |
387 | 388 |
388 void ClientUsageTracker::UpdateUsageCache( | 389 void ClientUsageTracker::UpdateUsageCache( |
389 const GURL& origin, int64 delta) { | 390 const GURL& origin, int64 delta) { |
390 std::string host = net::GetHostOrSpecFromURL(origin); | 391 std::string host = net::GetHostOrSpecFromURL(origin); |
391 if (cached_hosts_.find(host) != cached_hosts_.end()) { | 392 if (cached_hosts_.find(host) != cached_hosts_.end()) { |
392 cached_usage_[host][origin] += delta; | 393 cached_usage_[host][origin] += delta; |
393 global_usage_ += delta; | 394 global_usage_ += delta; |
394 if (global_unlimited_usage_is_valid_ && IsStorageUnlimited(origin)) | 395 if (global_unlimited_usage_is_valid_ && IsStorageUnlimited(origin)) |
395 global_unlimited_usage_ += delta; | 396 global_unlimited_usage_ += delta; |
396 DCHECK_GE(cached_usage_[host][origin], 0); | 397 DCHECK_GE(cached_usage_[host][origin], 0); |
397 DCHECK_GE(global_usage_, 0); | 398 DCHECK_GE(global_usage_, 0); |
398 return; | 399 return; |
399 } | 400 } |
400 | 401 |
401 // We don't know about this host yet, so populate our cache for it. | 402 // We don't know about this host yet, so populate our cache for it. |
402 GetHostUsage(host, | 403 GetHostUsage(host, |
403 NewCallback(this, &ClientUsageTracker::NoopHostUsageCallback)); | 404 base::Bind(&ClientUsageTracker::NoopHostUsageCallback, |
| 405 base::Unretained(this))); |
404 } | 406 } |
405 | 407 |
406 void ClientUsageTracker::GetCachedHostsUsage( | 408 void ClientUsageTracker::GetCachedHostsUsage( |
407 std::map<std::string, int64>* host_usage) const { | 409 std::map<std::string, int64>* host_usage) const { |
408 DCHECK(host_usage); | 410 DCHECK(host_usage); |
409 for (HostUsageMap::const_iterator host_iter = cached_usage_.begin(); | 411 for (HostUsageMap::const_iterator host_iter = cached_usage_.begin(); |
410 host_iter != cached_usage_.end(); host_iter++) { | 412 host_iter != cached_usage_.end(); host_iter++) { |
411 host_usage->operator[](host_iter->first) += | 413 host_usage->operator[](host_iter->first) += |
412 GetCachedHostUsage(host_iter->first); | 414 GetCachedHostUsage(host_iter->first); |
413 } | 415 } |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
508 void ClientUsageTracker::NoopHostUsageCallback( | 510 void ClientUsageTracker::NoopHostUsageCallback( |
509 const std::string& host, StorageType type, int64 usage) { | 511 const std::string& host, StorageType type, int64 usage) { |
510 } | 512 } |
511 | 513 |
512 bool ClientUsageTracker::IsStorageUnlimited(const GURL& origin) const { | 514 bool ClientUsageTracker::IsStorageUnlimited(const GURL& origin) const { |
513 return special_storage_policy_.get() && | 515 return special_storage_policy_.get() && |
514 special_storage_policy_->IsStorageUnlimited(origin); | 516 special_storage_policy_->IsStorageUnlimited(origin); |
515 } | 517 } |
516 | 518 |
517 } // namespace quota | 519 } // namespace quota |
OLD | NEW |