 Chromium Code Reviews
 Chromium Code Reviews Issue 12398008:
  Purge in-memory localStorage areas if the # of areas exceeds the limit  (Closed) 
  Base URL: svn://svn.chromium.org/chrome/trunk/src
    
  
    Issue 12398008:
  Purge in-memory localStorage areas if the # of areas exceeds the limit  (Closed) 
  Base URL: svn://svn.chromium.org/chrome/trunk/src| OLD | NEW | 
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/dom_storage/dom_storage_context.h" | 5 #include "webkit/dom_storage/dom_storage_context.h" | 
| 6 | 6 | 
| 7 #include "base/bind.h" | 7 #include "base/bind.h" | 
| 8 #include "base/bind_helpers.h" | 8 #include "base/bind_helpers.h" | 
| 9 #include "base/file_util.h" | 9 #include "base/file_util.h" | 
| 10 #include "base/guid.h" | 10 #include "base/guid.h" | 
| 11 #include "base/location.h" | 11 #include "base/location.h" | 
| 12 #include "base/time.h" | 12 #include "base/time.h" | 
| 13 #include "webkit/dom_storage/dom_storage_area.h" | 13 #include "webkit/dom_storage/dom_storage_area.h" | 
| 14 #include "webkit/dom_storage/dom_storage_database.h" | 14 #include "webkit/dom_storage/dom_storage_database.h" | 
| 15 #include "webkit/dom_storage/dom_storage_host.h" | |
| 15 #include "webkit/dom_storage/dom_storage_namespace.h" | 16 #include "webkit/dom_storage/dom_storage_namespace.h" | 
| 16 #include "webkit/dom_storage/dom_storage_task_runner.h" | 17 #include "webkit/dom_storage/dom_storage_task_runner.h" | 
| 17 #include "webkit/dom_storage/dom_storage_types.h" | 18 #include "webkit/dom_storage/dom_storage_types.h" | 
| 18 #include "webkit/dom_storage/session_storage_database.h" | 19 #include "webkit/dom_storage/session_storage_database.h" | 
| 19 #include "webkit/quota/special_storage_policy.h" | 20 #include "webkit/quota/special_storage_policy.h" | 
| 20 | 21 | 
| 21 using file_util::FileEnumerator; | 22 using file_util::FileEnumerator; | 
| 22 | 23 | 
| 23 namespace dom_storage { | 24 namespace dom_storage { | 
| 24 | 25 | 
| 25 static const int kSessionStoraceScavengingSeconds = 60; | 26 static const int kSessionStoraceScavengingSeconds = 60; | 
| 26 | 27 | 
| 27 DomStorageContext::DomStorageContext( | 28 DomStorageContext::DomStorageContext( | 
| 28 const base::FilePath& localstorage_directory, | 29 const base::FilePath& localstorage_directory, | 
| 29 const base::FilePath& sessionstorage_directory, | 30 const base::FilePath& sessionstorage_directory, | 
| 30 quota::SpecialStoragePolicy* special_storage_policy, | 31 quota::SpecialStoragePolicy* special_storage_policy, | 
| 31 DomStorageTaskRunner* task_runner) | 32 DomStorageTaskRunner* task_runner) | 
| 32 : localstorage_directory_(localstorage_directory), | 33 : localstorage_directory_(localstorage_directory), | 
| 33 sessionstorage_directory_(sessionstorage_directory), | 34 sessionstorage_directory_(sessionstorage_directory), | 
| 34 task_runner_(task_runner), | 35 task_runner_(task_runner), | 
| 35 is_shutdown_(false), | 36 is_shutdown_(false), | 
| 36 force_keep_session_state_(false), | 37 force_keep_session_state_(false), | 
| 37 special_storage_policy_(special_storage_policy), | 38 special_storage_policy_(special_storage_policy), | 
| 38 scavenging_started_(false) { | 39 scavenging_started_(false), | 
| 40 total_area_size_(-1) { | |
| 39 // AtomicSequenceNum starts at 0 but we want to start session | 41 // AtomicSequenceNum starts at 0 but we want to start session | 
| 40 // namespace ids at one since zero is reserved for the | 42 // namespace ids at one since zero is reserved for the | 
| 41 // kLocalStorageNamespaceId. | 43 // kLocalStorageNamespaceId. | 
| 42 session_id_sequence_.GetNext(); | 44 session_id_sequence_.GetNext(); | 
| 43 } | 45 } | 
| 44 | 46 | 
| 45 DomStorageContext::~DomStorageContext() { | 47 DomStorageContext::~DomStorageContext() { | 
| 46 if (session_storage_database_.get()) { | 48 if (session_storage_database_.get()) { | 
| 47 // SessionStorageDatabase shouldn't be deleted right away: deleting it will | 49 // SessionStorageDatabase shouldn't be deleted right away: deleting it will | 
| 48 // potentially involve waiting in leveldb::DBImpl::~DBImpl, and waiting | 50 // potentially involve waiting in leveldb::DBImpl::~DBImpl, and waiting | 
| (...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 207 void DomStorageContext::RemoveEventObserver(EventObserver* observer) { | 209 void DomStorageContext::RemoveEventObserver(EventObserver* observer) { | 
| 208 event_observers_.RemoveObserver(observer); | 210 event_observers_.RemoveObserver(observer); | 
| 209 } | 211 } | 
| 210 | 212 | 
| 211 void DomStorageContext::NotifyItemSet( | 213 void DomStorageContext::NotifyItemSet( | 
| 212 const DomStorageArea* area, | 214 const DomStorageArea* area, | 
| 213 const string16& key, | 215 const string16& key, | 
| 214 const string16& new_value, | 216 const string16& new_value, | 
| 215 const NullableString16& old_value, | 217 const NullableString16& old_value, | 
| 216 const GURL& page_url) { | 218 const GURL& page_url) { | 
| 219 InvalidateInMemoryAreaSize(); | |
| 217 FOR_EACH_OBSERVER( | 220 FOR_EACH_OBSERVER( | 
| 218 EventObserver, event_observers_, | 221 EventObserver, event_observers_, | 
| 219 OnDomStorageItemSet(area, key, new_value, old_value, page_url)); | 222 OnDomStorageItemSet(area, key, new_value, old_value, page_url)); | 
| 220 } | 223 } | 
| 221 | 224 | 
| 222 void DomStorageContext::NotifyItemRemoved( | 225 void DomStorageContext::NotifyItemRemoved( | 
| 223 const DomStorageArea* area, | 226 const DomStorageArea* area, | 
| 224 const string16& key, | 227 const string16& key, | 
| 225 const string16& old_value, | 228 const string16& old_value, | 
| 226 const GURL& page_url) { | 229 const GURL& page_url) { | 
| 230 InvalidateInMemoryAreaSize(); | |
| 227 FOR_EACH_OBSERVER( | 231 FOR_EACH_OBSERVER( | 
| 228 EventObserver, event_observers_, | 232 EventObserver, event_observers_, | 
| 229 OnDomStorageItemRemoved(area, key, old_value, page_url)); | 233 OnDomStorageItemRemoved(area, key, old_value, page_url)); | 
| 230 } | 234 } | 
| 231 | 235 | 
| 232 void DomStorageContext::NotifyAreaCleared( | 236 void DomStorageContext::NotifyAreaCleared( | 
| 233 const DomStorageArea* area, | 237 const DomStorageArea* area, | 
| 234 const GURL& page_url) { | 238 const GURL& page_url) { | 
| 239 InvalidateInMemoryAreaSize(); | |
| 235 FOR_EACH_OBSERVER( | 240 FOR_EACH_OBSERVER( | 
| 236 EventObserver, event_observers_, | 241 EventObserver, event_observers_, | 
| 237 OnDomStorageAreaCleared(area, page_url)); | 242 OnDomStorageAreaCleared(area, page_url)); | 
| 238 } | 243 } | 
| 239 | 244 | 
| 240 std::string DomStorageContext::AllocatePersistentSessionId() { | 245 std::string DomStorageContext::AllocatePersistentSessionId() { | 
| 241 std::string guid = base::GenerateGUID(); | 246 std::string guid = base::GenerateGUID(); | 
| 242 std::replace(guid.begin(), guid.end(), '-', '_'); | 247 std::replace(guid.begin(), guid.end(), '-', '_'); | 
| 243 return guid; | 248 return guid; | 
| 244 } | 249 } | 
| (...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 345 } | 350 } | 
| 346 | 351 | 
| 347 void DomStorageContext::StartScavengingUnusedSessionStorage() { | 352 void DomStorageContext::StartScavengingUnusedSessionStorage() { | 
| 348 if (session_storage_database_.get()) { | 353 if (session_storage_database_.get()) { | 
| 349 task_runner_->PostDelayedTask( | 354 task_runner_->PostDelayedTask( | 
| 350 FROM_HERE, base::Bind(&DomStorageContext::FindUnusedNamespaces, this), | 355 FROM_HERE, base::Bind(&DomStorageContext::FindUnusedNamespaces, this), | 
| 351 base::TimeDelta::FromSeconds(kSessionStoraceScavengingSeconds)); | 356 base::TimeDelta::FromSeconds(kSessionStoraceScavengingSeconds)); | 
| 352 } | 357 } | 
| 353 } | 358 } | 
| 354 | 359 | 
| 360 bool DomStorageContext::CanSetItem( | |
| 361 DomStorageHost* host, | |
| 362 int connection_id, | |
| 363 const string16& key, | |
| 364 const string16& value) { | |
| 365 DCHECK(host); | |
| 366 if (GetRemainingCacheSize() >= static_cast<int64>(kPerAreaQuota)) | |
| 367 return true; | |
| 368 // Go through the slower check only if the memory is getting tight. | |
| 369 // TODO(kinuko,michaeln): Purge cache memory if we're caching too many areas. | |
| 370 NullableString16 old_value = host->GetAreaItem(connection_id, key); | |
| 371 int64 increase = value.size(); | |
| 372 if (old_value.is_null()) | |
| 373 increase += key.size(); | |
| 374 else | |
| 375 increase -= old_value.string().size(); | |
| 376 return static_cast<int64>(increase * sizeof(char16)) <= | |
| 377 GetRemainingCacheSize(); | |
| 378 } | |
| 379 | |
| 355 void DomStorageContext::FindUnusedNamespaces() { | 380 void DomStorageContext::FindUnusedNamespaces() { | 
| 356 DCHECK(session_storage_database_.get()); | 381 DCHECK(session_storage_database_.get()); | 
| 357 if (scavenging_started_) | 382 if (scavenging_started_) | 
| 358 return; | 383 return; | 
| 359 scavenging_started_ = true; | 384 scavenging_started_ = true; | 
| 360 std::set<std::string> namespace_ids_in_use; | 385 std::set<std::string> namespace_ids_in_use; | 
| 361 for (StorageNamespaceMap::const_iterator it = namespaces_.begin(); | 386 for (StorageNamespaceMap::const_iterator it = namespaces_.begin(); | 
| 362 it != namespaces_.end(); ++it) | 387 it != namespaces_.end(); ++it) | 
| 363 namespace_ids_in_use.insert(it->second->persistent_namespace_id()); | 388 namespace_ids_in_use.insert(it->second->persistent_namespace_id()); | 
| 364 std::set<std::string> protected_persistent_session_ids; | 389 std::set<std::string> protected_persistent_session_ids; | 
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 414 deletable_persistent_namespace_ids_.pop_back(); | 439 deletable_persistent_namespace_ids_.pop_back(); | 
| 415 if (!deletable_persistent_namespace_ids_.empty()) { | 440 if (!deletable_persistent_namespace_ids_.empty()) { | 
| 416 task_runner_->PostDelayedTask( | 441 task_runner_->PostDelayedTask( | 
| 417 FROM_HERE, base::Bind( | 442 FROM_HERE, base::Bind( | 
| 418 &DomStorageContext::DeleteNextUnusedNamespace, | 443 &DomStorageContext::DeleteNextUnusedNamespace, | 
| 419 this), | 444 this), | 
| 420 base::TimeDelta::FromSeconds(kSessionStoraceScavengingSeconds)); | 445 base::TimeDelta::FromSeconds(kSessionStoraceScavengingSeconds)); | 
| 421 } | 446 } | 
| 422 } | 447 } | 
| 423 | 448 | 
| 449 int64 DomStorageContext::GetRemainingCacheSize() { | |
| 450 if (total_area_size_ < 0) { | |
| 
Mike West
2013/03/06 13:41:11
I don't see where you're updating total_area_size_
 | |
| 451 total_area_size_ = 0; | |
| 452 // Naively add-up the area size for each connection. | |
| 453 std::set<DomStorageArea*> visited_areas; | |
| 454 for (StorageNamespaceMap::const_iterator itr = namespaces_.begin(); | |
| 455 itr != namespaces_.end(); ++itr) { | |
| 456 const DomStorageNamespace::AreaMap& map = itr->second->GetAreaMap(); | |
| 457 for (DomStorageNamespace::AreaMap::const_iterator map_itr = map.begin(); | |
| 458 map_itr != map.end(); ++map_itr) { | |
| 459 // We don't care open_count_ here, since we don't purge memory (yet). | |
| 460 DomStorageArea* area = map_itr->second.area_; | |
| 461 DCHECK(area); | |
| 462 if (visited_areas.insert(area).second) | |
| 463 total_area_size_ += area->GetMapSize(); | |
| 464 } | |
| 465 } | |
| 466 } | |
| 467 return kMaxInMemoryAreaSize - total_area_size_; | |
| 468 } | |
| 469 | |
| 424 } // namespace dom_storage | 470 } // namespace dom_storage | 
| OLD | NEW |