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