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

Side by Side Diff: net/sdch/sdch_owner.cc

Issue 881413003: Make SDCH dictionaries persistent across browser restart. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Incorporated Matt's comments. Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698