Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 "net/sdch/sdch_owner.h" | 5 #include "net/sdch/sdch_owner.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/metrics/histogram_macros.h" | 8 #include "base/metrics/histogram_macros.h" |
| 9 #include "base/prefs/persistent_pref_store.h" | |
| 10 #include "base/prefs/value_map_pref_store.h" | |
| 9 #include "base/time/default_clock.h" | 11 #include "base/time/default_clock.h" |
| 12 #include "base/values.h" | |
| 10 #include "net/base/sdch_manager.h" | 13 #include "net/base/sdch_manager.h" |
| 11 #include "net/base/sdch_net_log_params.h" | 14 #include "net/base/sdch_net_log_params.h" |
| 12 | 15 |
| 16 namespace net { | |
| 17 | |
| 13 namespace { | 18 namespace { |
| 14 | 19 |
| 15 enum DictionaryFate { | 20 enum DictionaryFate { |
| 16 // A Get-Dictionary header wasn't acted on. | 21 // A Get-Dictionary header wasn't acted on. |
| 17 DICTIONARY_FATE_GET_IGNORED = 1, | 22 DICTIONARY_FATE_GET_IGNORED = 1, |
| 18 | 23 |
| 19 // A fetch was attempted, but failed. | 24 // A fetch was attempted, but failed. |
| 20 // TODO(rdsmith): Actually record this case. | 25 // TODO(rdsmith): Actually record this case. |
| 21 DICTIONARY_FATE_FETCH_FAILED = 2, | 26 DICTIONARY_FATE_FETCH_FAILED = 2, |
| 22 | 27 |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 34 | 39 |
| 35 // A dictionary was evicted by memory pressure. | 40 // A dictionary was evicted by memory pressure. |
| 36 DICTIONARY_FATE_EVICT_FOR_MEMORY = 7, | 41 DICTIONARY_FATE_EVICT_FOR_MEMORY = 7, |
| 37 | 42 |
| 38 // A dictionary was evicted on destruction. | 43 // A dictionary was evicted on destruction. |
| 39 DICTIONARY_FATE_EVICT_FOR_DESTRUCTION = 8, | 44 DICTIONARY_FATE_EVICT_FOR_DESTRUCTION = 8, |
| 40 | 45 |
| 41 DICTIONARY_FATE_MAX = 9 | 46 DICTIONARY_FATE_MAX = 9 |
| 42 }; | 47 }; |
| 43 | 48 |
| 49 struct FetchData : public SdchDictionaryFetcher::Data { | |
| 50 ~FetchData() override {} | |
| 51 | |
| 52 base::Time last_used; | |
| 53 int use_count; | |
| 54 | |
| 55 FetchData() : use_count(0) {} | |
| 56 FetchData(const base::Time& last_used, int use_count) | |
| 57 : last_used(last_used), use_count(use_count) {} | |
| 58 | |
| 59 private: | |
| 60 DISALLOW_COPY_AND_ASSIGN(FetchData); | |
| 61 }; | |
| 62 | |
| 63 // Dictionaries that haven't been touched in 24 hours may be evicted | |
| 64 // to make room for new dictionaries. | |
| 65 const int kHoursFreshSinceLastUsed = 24; | |
| 66 | |
| 67 // Newly added dictionaries are consider to have been last touched 23 hours | |
| 68 // ago, so they aren't kept around long if they're a one-off, but dictionary | |
| 69 // loading doesn't thrash if many dictionaries are seen in quick succession. | |
| 70 const int kInitialHoursSinceLastUsed = 23; | |
|
mmenke
2015/02/02 16:35:28
May be beyond the scoped of this CL, but this seem
Randy Smith (Not in Mondays)
2015/02/04 19:29:03
No argument, but ...
mmenke
2015/02/04 19:53:46
We're already recording the use_count, so I was th
| |
| 71 | |
| 44 void RecordDictionaryFate(enum DictionaryFate fate) { | 72 void RecordDictionaryFate(enum DictionaryFate fate) { |
| 45 UMA_HISTOGRAM_ENUMERATION("Sdch3.DictionaryFate", fate, DICTIONARY_FATE_MAX); | 73 UMA_HISTOGRAM_ENUMERATION("Sdch3.DictionaryFate", fate, DICTIONARY_FATE_MAX); |
| 46 } | 74 } |
| 47 | 75 |
| 48 void RecordDictionaryEviction(int use_count, DictionaryFate fate) { | 76 void RecordDictionaryEviction(int use_count, DictionaryFate fate) { |
| 49 DCHECK(fate == DICTIONARY_FATE_EVICT_FOR_DICT || | 77 DCHECK(fate == DICTIONARY_FATE_EVICT_FOR_DICT || |
| 50 fate == DICTIONARY_FATE_EVICT_FOR_MEMORY || | 78 fate == DICTIONARY_FATE_EVICT_FOR_MEMORY || |
| 51 fate == DICTIONARY_FATE_EVICT_FOR_DESTRUCTION); | 79 fate == DICTIONARY_FATE_EVICT_FOR_DESTRUCTION); |
| 52 | 80 |
| 53 UMA_HISTOGRAM_COUNTS_100("Sdch3.DictionaryUseCount", use_count); | 81 UMA_HISTOGRAM_COUNTS_100("Sdch3.DictionaryUseCount", use_count); |
| 54 RecordDictionaryFate(fate); | 82 RecordDictionaryFate(fate); |
| 55 } | 83 } |
| 56 | 84 |
| 85 // Schema specifications and access routines. | |
| 86 | |
| 87 // The persistent prefs store is conceptually shared with any other network | |
| 88 // stack systems that want to persist data over browser restarts, and so | |
| 89 // use of it must be namespace restricted. | |
| 90 // Schema: | |
| 91 // pref_store_->GetValue(kPreferenceName) -> Dictionary { | |
| 92 // 'version' -> 1 [int] | |
| 93 // 'dictionaries' -> Dictionary { | |
| 94 // server_hash -> { | |
| 95 // 'url' -> URL [string] | |
| 96 // 'last_used' -> seconds since unix epoch [double] | |
| 97 // 'use_count' -> use count [int] | |
| 98 // } | |
| 99 // } | |
| 100 const char kPreferenceName[] = "SDCH"; | |
| 101 const char kVersionName[] = "version"; | |
| 102 const char kDictionariesName[] = "dictionaries"; | |
| 103 const char kDictionaryUrlName[] = "url"; | |
| 104 const char kDictionaryLastUsedName[] = "last_used"; | |
| 105 const char kDictionaryUseCountName[] = "use_count"; | |
| 106 | |
| 107 const int kVersion = 1; | |
| 108 | |
| 109 // This function returns store[kPreferenceName] | |
| 110 base::DictionaryValue* GetPersistentStoreMap(WriteablePrefStore* store) { | |
| 111 base::Value* result = nullptr; | |
| 112 bool success = store->GetMutableValue(kPreferenceName, &result); | |
|
Bernhard Bauer
2015/02/02 14:39:46
Mutating the value returned by GetMutableValue wil
Randy Smith (Not in Mondays)
2015/02/04 19:29:03
Ah! Thank you for the heads up. I can't use Scop
Bernhard Bauer
2015/02/04 21:48:00
Yes, that's what I meant actually (I just mentione
| |
| 113 DCHECK(success); | |
| 114 | |
| 115 base::DictionaryValue* dictionary_result = nullptr; | |
| 116 success = result->GetAsDictionary(&dictionary_result); | |
| 117 DCHECK(success); | |
| 118 DCHECK(dictionary_result); | |
| 119 | |
| 120 return dictionary_result; | |
| 121 } | |
| 122 | |
| 123 // This function returns store[kPreferenceName/kDictionaries] | |
| 124 base::DictionaryValue* GetPersistentStoreDictionaryMap( | |
| 125 WriteablePrefStore* store) { | |
| 126 base::DictionaryValue* dictionary_result = nullptr; | |
| 127 bool success = GetPersistentStoreMap(store) | |
| 128 ->GetDictionary(kDictionariesName, &dictionary_result); | |
| 129 DCHECK(success); | |
| 130 DCHECK(dictionary_result); | |
| 131 | |
| 132 return dictionary_result; | |
| 133 } | |
| 134 | |
| 135 // This function initializes a pref store with an empty version of the | |
| 136 // above schema, removing anything previously in the store under | |
| 137 // kPreferenceName. | |
| 138 void InitializePersistentStore(WriteablePrefStore* store) { | |
| 139 base::DictionaryValue* empty_store(new base::DictionaryValue); | |
| 140 empty_store->SetInteger(kVersionName, kVersion); | |
| 141 empty_store->Set(kDictionariesName, | |
| 142 make_scoped_ptr(new base::DictionaryValue)); | |
| 143 store->SetValue(kPreferenceName, empty_store); | |
| 144 } | |
| 145 | |
| 57 } // namespace | 146 } // namespace |
| 58 | 147 |
| 59 namespace net { | |
| 60 | |
| 61 // Adjust SDCH limits downwards for mobile. | 148 // Adjust SDCH limits downwards for mobile. |
| 62 #if defined(OS_ANDROID) || defined(OS_IOS) | 149 #if defined(OS_ANDROID) || defined(OS_IOS) |
| 63 // static | 150 // static |
| 64 const size_t SdchOwner::kMaxTotalDictionarySize = 1000 * 1000; | 151 const size_t SdchOwner::kMaxTotalDictionarySize = 1000 * 1000; |
| 65 #else | 152 #else |
| 66 // static | 153 // static |
| 67 const size_t SdchOwner::kMaxTotalDictionarySize = 20 * 1000 * 1000; | 154 const size_t SdchOwner::kMaxTotalDictionarySize = 20 * 1000 * 1000; |
| 68 #endif | 155 #endif |
| 69 | 156 |
| 70 // Somewhat arbitrary, but we assume a dictionary smaller than | 157 // Somewhat arbitrary, but we assume a dictionary smaller than |
| 71 // 50K isn't going to do anyone any good. Note that this still doesn't | 158 // 50K isn't going to do anyone any good. Note that this still doesn't |
| 72 // prevent download and addition unless there is less than this | 159 // prevent download and addition unless there is less than this |
| 73 // amount of space available in storage. | 160 // amount of space available in storage. |
| 74 const size_t SdchOwner::kMinSpaceForDictionaryFetch = 50 * 1000; | 161 const size_t SdchOwner::kMinSpaceForDictionaryFetch = 50 * 1000; |
| 75 | 162 |
| 76 SdchOwner::SdchOwner(net::SdchManager* sdch_manager, | 163 SdchOwner::SdchOwner(net::SdchManager* sdch_manager, |
| 77 net::URLRequestContext* context) | 164 net::URLRequestContext* context) |
| 78 : manager_(sdch_manager), | 165 : manager_(sdch_manager), |
| 79 fetcher_(context, | 166 fetcher_(context, |
| 80 base::Bind(&SdchOwner::OnDictionaryFetched, | 167 base::Bind(&SdchOwner::OnDictionaryFetched, |
| 81 // Because |fetcher_| is owned by SdchOwner, the | 168 // Because |fetcher_| is owned by SdchOwner, the |
| 82 // SdchOwner object will be available for the lifetime | 169 // SdchOwner object will be available for the lifetime |
| 83 // of |fetcher_|. | 170 // of |fetcher_|. |
| 84 base::Unretained(this))), | 171 base::Unretained(this))), |
| 85 total_dictionary_bytes_(0), | 172 total_dictionary_bytes_(0), |
| 86 clock_(new base::DefaultClock), | 173 clock_(new base::DefaultClock), |
| 87 max_total_dictionary_size_(kMaxTotalDictionarySize), | 174 max_total_dictionary_size_(kMaxTotalDictionarySize), |
| 88 min_space_for_dictionary_fetch_(kMinSpaceForDictionaryFetch), | 175 min_space_for_dictionary_fetch_(kMinSpaceForDictionaryFetch), |
| 176 in_memory_pref_store_(new ValueMapPrefStore()), | |
| 177 persistent_pref_store_(nullptr), | |
| 178 pref_store_(in_memory_pref_store_.get()), | |
| 89 memory_pressure_listener_( | 179 memory_pressure_listener_( |
| 90 base::Bind(&SdchOwner::OnMemoryPressure, | 180 base::Bind(&SdchOwner::OnMemoryPressure, |
| 91 // Because |memory_pressure_listener_| is owned by | 181 // Because |memory_pressure_listener_| is owned by |
| 92 // SdchOwner, the SdchOwner object will be available | 182 // SdchOwner, the SdchOwner object will be available |
| 93 // for the lifetime of |memory_pressure_listener_|. | 183 // for the lifetime of |memory_pressure_listener_|. |
| 94 base::Unretained(this))) { | 184 base::Unretained(this))) { |
| 95 manager_->AddObserver(this); | 185 manager_->AddObserver(this); |
| 186 InitializePersistentStore(pref_store_); | |
| 96 } | 187 } |
| 97 | 188 |
| 98 SdchOwner::~SdchOwner() { | 189 SdchOwner::~SdchOwner() { |
| 99 for (auto it = local_dictionary_info_.begin(); | 190 for (auto it = local_dictionary_info_.begin(); |
| 100 it != local_dictionary_info_.end(); ++it) { | 191 it != local_dictionary_info_.end(); ++it) { |
| 101 RecordDictionaryEviction(it->second.use_count, | 192 RecordDictionaryEviction(it->second.use_count, |
| 102 DICTIONARY_FATE_EVICT_FOR_DESTRUCTION); | 193 DICTIONARY_FATE_EVICT_FOR_DESTRUCTION); |
| 103 } | 194 } |
| 104 manager_->RemoveObserver(this); | 195 manager_->RemoveObserver(this); |
| 196 if (pref_store_) | |
| 197 pref_store_->RemoveObserver(this); | |
| 198 } | |
| 199 | |
| 200 void SdchOwner::EnablePersistentStorage(PersistentPrefStore* pref_store) { | |
| 201 DCHECK(!persistent_pref_store_); | |
|
mmenke
2015/02/02 16:35:28
include base/logging.h
Randy Smith (Not in Mondays)
2015/02/04 19:29:03
Done.
| |
| 202 persistent_pref_store_ = pref_store; | |
| 203 persistent_pref_store_->AddObserver(this); | |
| 204 | |
| 205 if (persistent_pref_store_->IsInitializationComplete()) | |
| 206 OnInitializationCompleted(true); | |
| 105 } | 207 } |
| 106 | 208 |
| 107 void SdchOwner::SetMaxTotalDictionarySize(size_t max_total_dictionary_size) { | 209 void SdchOwner::SetMaxTotalDictionarySize(size_t max_total_dictionary_size) { |
| 108 max_total_dictionary_size_ = max_total_dictionary_size; | 210 max_total_dictionary_size_ = max_total_dictionary_size; |
| 109 } | 211 } |
| 110 | 212 |
| 111 void SdchOwner::SetMinSpaceForDictionaryFetch( | 213 void SdchOwner::SetMinSpaceForDictionaryFetch( |
| 112 size_t min_space_for_dictionary_fetch) { | 214 size_t min_space_for_dictionary_fetch) { |
| 113 min_space_for_dictionary_fetch_ = min_space_for_dictionary_fetch; | 215 min_space_for_dictionary_fetch_ = min_space_for_dictionary_fetch; |
| 114 } | 216 } |
| 115 | 217 |
| 116 void SdchOwner::OnDictionaryFetched(const std::string& dictionary_text, | 218 void SdchOwner::OnDictionaryFetched( |
| 117 const GURL& dictionary_url, | 219 const std::string& dictionary_text, |
| 118 const net::BoundNetLog& net_log) { | 220 const GURL& dictionary_url, |
| 221 scoped_ptr<SdchDictionaryFetcher::Data> extra_data, | |
| 222 const net::BoundNetLog& net_log) { | |
| 119 struct DictionaryItem { | 223 struct DictionaryItem { |
| 120 base::Time last_used; | 224 base::Time last_used; |
| 121 std::string server_hash; | 225 std::string server_hash; |
| 122 int use_count; | 226 int use_count; |
| 123 size_t dictionary_size; | 227 size_t dictionary_size; |
| 124 | 228 |
| 125 DictionaryItem() : use_count(0), dictionary_size(0) {} | 229 DictionaryItem() : use_count(0), dictionary_size(0) {} |
| 126 DictionaryItem(const base::Time& last_used, | 230 DictionaryItem(const base::Time& last_used, |
| 127 const std::string& server_hash, | 231 const std::string& server_hash, |
| 128 int use_count, | 232 int use_count, |
| 129 size_t dictionary_size) | 233 size_t dictionary_size) |
| 130 : last_used(last_used), | 234 : last_used(last_used), |
| 131 server_hash(server_hash), | 235 server_hash(server_hash), |
| 132 use_count(use_count), | 236 use_count(use_count), |
| 133 dictionary_size(dictionary_size) {} | 237 dictionary_size(dictionary_size) {} |
| 134 DictionaryItem(const DictionaryItem& rhs) = default; | 238 DictionaryItem(const DictionaryItem& rhs) = default; |
| 135 DictionaryItem& operator=(const DictionaryItem& rhs) = default; | 239 DictionaryItem& operator=(const DictionaryItem& rhs) = default; |
| 136 bool operator<(const DictionaryItem& rhs) const { | 240 bool operator<(const DictionaryItem& rhs) const { |
| 137 return last_used < rhs.last_used; | 241 return last_used < rhs.last_used; |
| 138 } | 242 } |
| 139 }; | 243 }; |
| 140 | 244 |
| 245 // Figure out if there is space for the incoming dictionary; evict | |
| 246 // stale dictionaries if needed to make space. | |
| 247 | |
| 141 std::vector<DictionaryItem> stale_dictionary_list; | 248 std::vector<DictionaryItem> stale_dictionary_list; |
| 142 size_t recoverable_bytes = 0; | 249 size_t recoverable_bytes = 0; |
| 143 base::Time stale_boundary(clock_->Now() - base::TimeDelta::FromDays(1)); | 250 base::Time stale_boundary( |
| 251 clock_->Now() - base::TimeDelta::FromHours(kHoursFreshSinceLastUsed)); | |
| 144 for (auto used_it = local_dictionary_info_.begin(); | 252 for (auto used_it = local_dictionary_info_.begin(); |
| 145 used_it != local_dictionary_info_.end(); ++used_it) { | 253 used_it != local_dictionary_info_.end(); ++used_it) { |
| 146 if (used_it->second.last_used < stale_boundary) { | 254 if (used_it->second.last_used < stale_boundary) { |
| 147 stale_dictionary_list.push_back( | 255 stale_dictionary_list.push_back( |
| 148 DictionaryItem(used_it->second.last_used, used_it->first, | 256 DictionaryItem(used_it->second.last_used, used_it->first, |
| 149 used_it->second.use_count, used_it->second.size)); | 257 used_it->second.use_count, used_it->second.size)); |
| 150 recoverable_bytes += used_it->second.size; | 258 recoverable_bytes += used_it->second.size; |
| 151 } | 259 } |
| 152 } | 260 } |
| 153 | 261 |
| 154 if (total_dictionary_bytes_ + dictionary_text.size() - recoverable_bytes > | 262 if (total_dictionary_bytes_ + dictionary_text.size() - recoverable_bytes > |
| 155 max_total_dictionary_size_) { | 263 max_total_dictionary_size_) { |
| 156 RecordDictionaryFate(DICTIONARY_FATE_FETCH_IGNORED_NO_SPACE); | 264 RecordDictionaryFate(DICTIONARY_FATE_FETCH_IGNORED_NO_SPACE); |
| 157 net::SdchManager::SdchErrorRecovery(SDCH_DICTIONARY_NO_ROOM); | 265 net::SdchManager::SdchErrorRecovery(SDCH_DICTIONARY_NO_ROOM); |
| 158 net_log.AddEvent(net::NetLog::TYPE_SDCH_DICTIONARY_ERROR, | 266 net_log.AddEvent(net::NetLog::TYPE_SDCH_DICTIONARY_ERROR, |
| 159 base::Bind(&net::NetLogSdchDictionaryFetchProblemCallback, | 267 base::Bind(&net::NetLogSdchDictionaryFetchProblemCallback, |
| 160 SDCH_DICTIONARY_NO_ROOM, dictionary_url, true)); | 268 SDCH_DICTIONARY_NO_ROOM, dictionary_url, true)); |
| 161 return; | 269 return; |
| 162 } | 270 } |
| 163 | 271 |
| 164 // Evict from oldest to youngest until we have space. | 272 base::DictionaryValue* pref_dictionary_map = |
| 273 GetPersistentStoreDictionaryMap(pref_store_); | |
| 274 | |
| 165 std::sort(stale_dictionary_list.begin(), stale_dictionary_list.end()); | 275 std::sort(stale_dictionary_list.begin(), stale_dictionary_list.end()); |
| 166 size_t avail_bytes = max_total_dictionary_size_ - total_dictionary_bytes_; | 276 size_t avail_bytes = max_total_dictionary_size_ - total_dictionary_bytes_; |
| 167 auto stale_it = stale_dictionary_list.begin(); | 277 auto stale_it = stale_dictionary_list.begin(); |
| 168 while (avail_bytes < dictionary_text.size() && | 278 while (avail_bytes < dictionary_text.size() && |
| 169 stale_it != stale_dictionary_list.end()) { | 279 stale_it != stale_dictionary_list.end()) { |
| 170 manager_->RemoveSdchDictionary(stale_it->server_hash); | 280 manager_->RemoveSdchDictionary(stale_it->server_hash); |
| 281 local_dictionary_info_.erase(stale_it->server_hash); | |
| 282 | |
| 283 DCHECK(pref_dictionary_map->HasKey(stale_it->server_hash)); | |
| 284 bool success = pref_dictionary_map->RemoveWithoutPathExpansion( | |
| 285 stale_it->server_hash, nullptr); | |
| 286 DCHECK(success); | |
| 287 | |
| 288 avail_bytes += stale_it->dictionary_size; | |
| 289 | |
| 171 RecordDictionaryEviction(stale_it->use_count, | 290 RecordDictionaryEviction(stale_it->use_count, |
| 172 DICTIONARY_FATE_EVICT_FOR_DICT); | 291 DICTIONARY_FATE_EVICT_FOR_DICT); |
| 173 local_dictionary_info_.erase(stale_it->server_hash); | 292 |
| 174 avail_bytes += stale_it->dictionary_size; | |
| 175 ++stale_it; | 293 ++stale_it; |
| 176 } | 294 } |
| 177 DCHECK(avail_bytes >= dictionary_text.size()); | 295 DCHECK(avail_bytes >= dictionary_text.size()); |
| 178 | 296 |
| 297 // Add the new dictionary. | |
| 179 std::string server_hash; | 298 std::string server_hash; |
| 180 net::SdchProblemCode rv = manager_->AddSdchDictionary( | 299 net::SdchProblemCode rv = manager_->AddSdchDictionary( |
| 181 dictionary_text, dictionary_url, &server_hash); | 300 dictionary_text, dictionary_url, &server_hash); |
| 182 if (rv != net::SDCH_OK) { | 301 if (rv != net::SDCH_OK) { |
| 183 RecordDictionaryFate(DICTIONARY_FATE_FETCH_MANAGER_REFUSED); | 302 RecordDictionaryFate(DICTIONARY_FATE_FETCH_MANAGER_REFUSED); |
| 184 net::SdchManager::SdchErrorRecovery(rv); | 303 net::SdchManager::SdchErrorRecovery(rv); |
| 185 net_log.AddEvent(net::NetLog::TYPE_SDCH_DICTIONARY_ERROR, | 304 net_log.AddEvent(net::NetLog::TYPE_SDCH_DICTIONARY_ERROR, |
| 186 base::Bind(&net::NetLogSdchDictionaryFetchProblemCallback, | 305 base::Bind(&net::NetLogSdchDictionaryFetchProblemCallback, |
| 187 rv, dictionary_url, true)); | 306 rv, dictionary_url, true)); |
| 188 return; | 307 return; |
| 189 } | 308 } |
| 190 | 309 |
| 191 RecordDictionaryFate(DICTIONARY_FATE_ADDED); | 310 RecordDictionaryFate(DICTIONARY_FATE_ADDED); |
| 192 | 311 |
| 312 total_dictionary_bytes_ += dictionary_text.size(); | |
| 313 | |
| 314 DictionaryInfo dictionary_info; | |
| 315 dictionary_info.size = dictionary_text.size(); | |
| 316 if (extra_data) { | |
| 317 // This dictionary was persisted from a previous instance of the | |
| 318 // browser; copy the usage information across from that instance. | |
| 319 FetchData* sdch_fetch_data = static_cast<FetchData*>(extra_data.get()); | |
|
Bernhard Bauer
2015/02/02 14:39:46
An alternative to passing this as SdchDictionaryFe
mmenke
2015/02/02 16:35:28
+1 to binding it...Actually, could make the protot
Randy Smith (Not in Mondays)
2015/02/04 19:29:03
Good point; that's an excellent idea that cleans u
Randy Smith (Not in Mondays)
2015/02/04 19:29:03
Yep. This makes several things much simpler. Tha
| |
| 320 dictionary_info.last_used = sdch_fetch_data->last_used; | |
| 321 dictionary_info.use_count = sdch_fetch_data->use_count; | |
| 322 } else { | |
| 323 // This is a newly added dictionary; the use count will be zero, and | |
| 324 // the last used time set to something that will avoid thrashing, but | |
| 325 // won't keep the dictionary in memory for too long if it's never used. | |
| 326 dictionary_info.last_used = | |
| 327 clock_->Now() - base::TimeDelta::FromHours(kInitialHoursSinceLastUsed); | |
| 328 dictionary_info.use_count = 0; | |
| 329 } | |
| 330 | |
| 193 DCHECK(local_dictionary_info_.end() == | 331 DCHECK(local_dictionary_info_.end() == |
| 194 local_dictionary_info_.find(server_hash)); | 332 local_dictionary_info_.find(server_hash)); |
| 195 total_dictionary_bytes_ += dictionary_text.size(); | 333 local_dictionary_info_[server_hash] = dictionary_info; |
| 196 local_dictionary_info_[server_hash] = DictionaryInfo( | 334 |
| 197 // Set the time last used to something to avoid thrashing, but not recent, | 335 // Record the addition in the pref store. |
| 198 // to avoid taking too much time/space with useless dictionaries/one-off | 336 scoped_ptr<base::DictionaryValue> dictionary_description( |
| 199 // visits to web sites. | 337 new base::DictionaryValue()); |
| 200 clock_->Now() - base::TimeDelta::FromHours(23), dictionary_text.size()); | 338 dictionary_description->SetString(kDictionaryUrlName, dictionary_url.spec()); |
| 339 dictionary_description->SetDouble(kDictionaryLastUsedName, | |
| 340 dictionary_info.last_used.ToDoubleT()); | |
| 341 dictionary_description->SetInteger(kDictionaryUseCountName, | |
| 342 dictionary_info.use_count); | |
| 343 pref_dictionary_map->Set(server_hash, dictionary_description.Pass()); | |
| 201 } | 344 } |
| 202 | 345 |
| 203 void SdchOwner::OnDictionaryUsed(SdchManager* manager, | 346 void SdchOwner::OnDictionaryUsed(SdchManager* manager, |
| 204 const std::string& server_hash) { | 347 const std::string& server_hash) { |
| 205 auto it = local_dictionary_info_.find(server_hash); | 348 auto it = local_dictionary_info_.find(server_hash); |
| 206 DCHECK(local_dictionary_info_.end() != it); | 349 DCHECK(local_dictionary_info_.end() != it); |
| 207 | 350 |
| 208 it->second.last_used = clock_->Now(); | 351 it->second.last_used = clock_->Now(); |
| 209 it->second.use_count++; | 352 it->second.use_count++; |
|
mmenke
2015/02/02 16:35:28
optional: I'm not a big fan of storing things twi
Randy Smith (Not in Mondays)
2015/02/04 19:29:03
Yeah, I had the same thought--just hadn't gotten t
| |
| 353 | |
| 354 base::DictionaryValue* pref_dictionary_map = | |
| 355 GetPersistentStoreDictionaryMap(pref_store_); | |
| 356 | |
| 357 pref_dictionary_map->SetInteger(server_hash + "/use_count", | |
| 358 it->second.use_count); | |
| 359 pref_dictionary_map->SetDouble(server_hash + "/last_used", | |
| 360 it->second.last_used.ToDoubleT()); | |
|
mmenke
2015/02/02 16:35:28
Shouldn't you be updating the dictionary fields yo
Randy Smith (Not in Mondays)
2015/02/04 19:29:03
I presume you mean "shouldn't you be using the con
| |
| 210 } | 361 } |
| 211 | 362 |
| 212 void SdchOwner::OnGetDictionary(net::SdchManager* manager, | 363 void SdchOwner::OnGetDictionary(net::SdchManager* manager, |
| 213 const GURL& request_url, | 364 const GURL& request_url, |
| 214 const GURL& dictionary_url) { | 365 const GURL& dictionary_url) { |
| 215 base::Time stale_boundary(clock_->Now() - base::TimeDelta::FromDays(1)); | 366 base::Time stale_boundary(clock_->Now() - base::TimeDelta::FromDays(1)); |
| 216 size_t avail_bytes = 0; | 367 size_t avail_bytes = 0; |
| 217 for (auto it = local_dictionary_info_.begin(); | 368 for (auto it = local_dictionary_info_.begin(); |
| 218 it != local_dictionary_info_.end(); ++it) { | 369 it != local_dictionary_info_.end(); ++it) { |
| 219 if (it->second.last_used < stale_boundary) | 370 if (it->second.last_used < stale_boundary) |
| 220 avail_bytes += it->second.size; | 371 avail_bytes += it->second.size; |
| 221 } | 372 } |
| 222 | 373 |
| 223 // Don't initiate the fetch if we wouldn't be able to store any | 374 // Don't initiate the fetch if we wouldn't be able to store any |
| 224 // reasonable dictionary. | 375 // reasonable dictionary. |
| 225 // TODO(rdsmith): Maybe do a HEAD request to figure out how much | 376 // TODO(rdsmith): Maybe do a HEAD request to figure out how much |
| 226 // storage we'd actually need? | 377 // storage we'd actually need? |
| 227 if (max_total_dictionary_size_ < (total_dictionary_bytes_ - avail_bytes + | 378 if (max_total_dictionary_size_ < (total_dictionary_bytes_ - avail_bytes + |
| 228 min_space_for_dictionary_fetch_)) { | 379 min_space_for_dictionary_fetch_)) { |
| 229 RecordDictionaryFate(DICTIONARY_FATE_GET_IGNORED); | 380 RecordDictionaryFate(DICTIONARY_FATE_GET_IGNORED); |
| 230 // TODO(rdsmith): Log a net-internals error. This requires | 381 // TODO(rdsmith): Log a net-internals error. This requires |
| 231 // SdchManager to forward the URLRequest that detected the | 382 // SdchManager to forward the URLRequest that detected the |
| 232 // Get-Dictionary header to its observers, which is tricky | 383 // Get-Dictionary header to its observers, which is tricky |
| 233 // because SdchManager is layered underneath URLRequest. | 384 // because SdchManager is layered underneath URLRequest. |
| 234 return; | 385 return; |
| 235 } | 386 } |
| 236 | 387 |
| 237 fetcher_.Schedule(dictionary_url); | 388 fetcher_.Schedule(dictionary_url, |
| 389 scoped_ptr<SdchDictionaryFetcher::Data>().Pass()); | |
| 238 } | 390 } |
| 239 | 391 |
| 240 void SdchOwner::OnClearDictionaries(net::SdchManager* manager) { | 392 void SdchOwner::OnClearDictionaries(net::SdchManager* manager) { |
| 241 total_dictionary_bytes_ = 0; | 393 total_dictionary_bytes_ = 0; |
| 242 local_dictionary_info_.clear(); | 394 local_dictionary_info_.clear(); |
| 243 fetcher_.Cancel(); | 395 fetcher_.Cancel(); |
| 396 | |
| 397 GetPersistentStoreMap(pref_store_) | |
| 398 ->Set(kDictionariesName, new base::DictionaryValue()); | |
| 399 } | |
| 400 | |
| 401 void SdchOwner::OnPrefValueChanged(const std::string& key) { | |
| 402 } | |
| 403 | |
| 404 void SdchOwner::OnInitializationCompleted(bool succeeded) { | |
| 405 // Error handling is simple: Don't read in any dictionaries and let | |
| 406 // normal user behavior fault them back in as needed. | |
| 407 // TODO(rdsmith): Decide whether to actually flush things to the | |
| 408 // persistent store in this case. | |
| 409 if (!succeeded) | |
| 410 return; | |
| 411 | |
| 412 // Load in what was stored before chrome exited previously. | |
| 413 const base::Value* sdch_persistence_value = nullptr; | |
| 414 const base::DictionaryValue* sdch_persistence_dictionary = nullptr; | |
| 415 | |
| 416 // The GetPersistentStore() routine above assumes data formatted | |
| 417 // according to the schema described at the top of this file. Since | |
| 418 // this data comes from disk, to avoid disk corruption resulting in | |
| 419 // persistent chrome errors this code avoids those assupmtions. | |
| 420 if (persistent_pref_store_->GetValue(kPreferenceName, | |
| 421 &sdch_persistence_value) && | |
| 422 sdch_persistence_value->GetAsDictionary(&sdch_persistence_dictionary)) { | |
| 423 LoadPersistedDictionaries(*sdch_persistence_dictionary); | |
| 424 } | |
| 425 | |
| 426 // Reset the persistent store and update it with the accumulated | |
| 427 // information from the local store. | |
| 428 InitializePersistentStore(persistent_pref_store_); | |
| 429 GetPersistentStoreDictionaryMap(persistent_pref_store_) | |
| 430 ->Swap(GetPersistentStoreDictionaryMap(in_memory_pref_store_.get())); | |
| 431 | |
| 432 // Switch the class pointer and free the old store. | |
| 433 pref_store_ = persistent_pref_store_; | |
| 434 in_memory_pref_store_ = nullptr; | |
| 244 } | 435 } |
| 245 | 436 |
| 246 void SdchOwner::SetClockForTesting(scoped_ptr<base::Clock> clock) { | 437 void SdchOwner::SetClockForTesting(scoped_ptr<base::Clock> clock) { |
| 247 clock_ = clock.Pass(); | 438 clock_ = clock.Pass(); |
| 248 } | 439 } |
| 249 | 440 |
| 250 void SdchOwner::OnMemoryPressure( | 441 void SdchOwner::OnMemoryPressure( |
| 251 base::MemoryPressureListener::MemoryPressureLevel level) { | 442 base::MemoryPressureListener::MemoryPressureLevel level) { |
| 252 DCHECK_NE(base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, level); | 443 DCHECK_NE(base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, level); |
| 253 | 444 |
| 254 for (auto it = local_dictionary_info_.begin(); | 445 for (auto it = local_dictionary_info_.begin(); |
| 255 it != local_dictionary_info_.end(); ++it) { | 446 it != local_dictionary_info_.end(); ++it) { |
| 256 RecordDictionaryEviction(it->second.use_count, | 447 RecordDictionaryEviction(it->second.use_count, |
| 257 DICTIONARY_FATE_EVICT_FOR_MEMORY); | 448 DICTIONARY_FATE_EVICT_FOR_MEMORY); |
| 258 } | 449 } |
| 259 | 450 |
| 260 // TODO(rdsmith): Make a distinction between moderate and critical | 451 // TODO(rdsmith): Make a distinction between moderate and critical |
| 261 // memory pressure. | 452 // memory pressure. |
| 262 manager_->ClearData(); | 453 manager_->ClearData(); |
| 263 } | 454 } |
| 264 | 455 |
| 456 bool SdchOwner::LoadPersistedDictionaries( | |
| 457 const base::DictionaryValue& persisted_info) { | |
| 458 // Any schema error will result in dropping the persisted info. | |
| 459 int version = 0; | |
| 460 if (!persisted_info.GetInteger(kVersionName, &version)) | |
| 461 return false; | |
| 462 | |
| 463 // TODO(rdsmith): Handle version upgrades. | |
|
mmenke
2015/02/02 16:35:28
Note: I'm not sure we care enough to do this. Ju
Randy Smith (Not in Mondays)
2015/02/04 19:29:03
Good point. I wasn't planning to do any work (bes
| |
| 464 if (version != 1) | |
| 465 return false; | |
| 466 | |
| 467 const base::DictionaryValue* dictionary_set = nullptr; | |
| 468 if (!persisted_info.GetDictionary(kDictionariesName, &dictionary_set)) | |
| 469 return false; | |
| 470 | |
| 471 for (base::DictionaryValue::Iterator dict_it(*dictionary_set); | |
| 472 !dict_it.IsAtEnd(); dict_it.Advance()) { | |
| 473 const base::DictionaryValue* dict_info = nullptr; | |
| 474 if (!dict_it.value().GetAsDictionary(&dict_info)) | |
| 475 return false; | |
|
mmenke
2015/02/02 16:35:28
Should we return early here? Currently, we schedu
Randy Smith (Not in Mondays)
2015/02/04 19:29:03
Good point. Done.
| |
| 476 | |
| 477 std::string url_string; | |
| 478 if (!dict_info->GetString(kDictionaryUrlName, &url_string)) | |
| 479 return false; | |
| 480 GURL dict_url(url_string); | |
| 481 | |
| 482 double last_used = 0.0; | |
|
mmenke
2015/02/02 16:35:28
No need to initialize this or use_count.
Randy Smith (Not in Mondays)
2015/02/04 19:29:03
Hmmm. That seems like it's relying on the interfa
| |
| 483 if (!dict_info->GetDouble(kDictionaryLastUsedName, &last_used)) | |
| 484 return false; | |
| 485 | |
| 486 int use_count = 0; | |
| 487 if (!dict_info->GetInteger(kDictionaryUseCountName, &use_count)) | |
| 488 return false; | |
| 489 | |
| 490 scoped_ptr<FetchData> data( | |
| 491 new FetchData(base::Time::FromDoubleT(last_used), use_count)); | |
| 492 | |
| 493 fetcher_.ScheduleReload(dict_url, data.Pass()); | |
| 494 } | |
| 495 | |
| 496 return true; | |
| 497 } | |
| 498 | |
| 265 } // namespace net | 499 } // namespace net |
| OLD | NEW |