Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / net / sdch / sdch_owner.cc
blobca9b5a77fab4fe3bd870fe86b797c0baf31db8fe
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
3 // found in the LICENSE file.
5 #include "net/sdch/sdch_owner.h"
7 #include "base/bind.h"
8 #include "base/debug/alias.h"
9 #include "base/logging.h"
10 #include "base/metrics/histogram_macros.h"
11 #include "base/prefs/persistent_pref_store.h"
12 #include "base/prefs/value_map_pref_store.h"
13 #include "base/strings/string_util.h"
14 #include "base/time/default_clock.h"
15 #include "base/values.h"
16 #include "net/base/sdch_manager.h"
17 #include "net/base/sdch_net_log_params.h"
19 namespace net {
21 namespace {
23 enum PersistenceFailureReason {
24 // File didn't exist; is being created.
25 PERSISTENCE_FAILURE_REASON_NO_FILE = 1,
27 // Error reading in information, but should be able to write.
28 PERSISTENCE_FAILURE_REASON_READ_FAILED = 2,
30 // Error leading to abort on attempted persistence.
31 PERSISTENCE_FAILURE_REASON_WRITE_FAILED = 3,
33 PERSISTENCE_FAILURE_REASON_MAX = 4
36 // Dictionaries that haven't been touched in 24 hours may be evicted
37 // to make room for new dictionaries.
38 const int kFreshnessLifetimeHours = 24;
40 // Dictionaries that have never been used only stay fresh for one hour.
41 const int kNeverUsedFreshnessLifetimeHours = 1;
43 void RecordPersistenceFailure(PersistenceFailureReason failure_reason) {
44 UMA_HISTOGRAM_ENUMERATION("Sdch3.PersistenceFailureReason", failure_reason,
45 PERSISTENCE_FAILURE_REASON_MAX);
48 // Schema specifications and access routines.
50 // The persistent prefs store is conceptually shared with any other network
51 // stack systems that want to persist data over browser restarts, and so
52 // use of it must be namespace restricted.
53 // Schema:
54 // pref_store_->GetValue(kPreferenceName) -> Dictionary {
55 // 'version' -> 1 [int]
56 // 'dictionaries' -> Dictionary {
57 // server_hash -> {
58 // 'url' -> URL [string]
59 // 'last_used' -> seconds since unix epoch [double]
60 // 'use_count' -> use count [int]
61 // 'size' -> size [int]
62 // }
63 // }
64 const char kPreferenceName[] = "SDCH";
65 const char kVersionKey[] = "version";
66 const char kDictionariesKey[] = "dictionaries";
67 const char kDictionaryUrlKey[] = "url";
68 const char kDictionaryLastUsedKey[] = "last_used";
69 const char kDictionaryUseCountKey[] = "use_count";
70 const char kDictionarySizeKey[] = "size";
72 const int kVersion = 1;
74 // This function returns store[kPreferenceName/kDictionariesKey]. The caller
75 // is responsible for making sure any needed calls to
76 // |store->ReportValueChanged()| occur.
77 base::DictionaryValue* GetPersistentStoreDictionaryMap(
78 WriteablePrefStore* store) {
79 base::Value* result = nullptr;
80 bool success = store->GetMutableValue(kPreferenceName, &result);
81 DCHECK(success);
83 base::DictionaryValue* preference_dictionary = nullptr;
84 success = result->GetAsDictionary(&preference_dictionary);
85 DCHECK(success);
86 DCHECK(preference_dictionary);
88 base::DictionaryValue* dictionary_list_dictionary = nullptr;
89 success = preference_dictionary->GetDictionary(kDictionariesKey,
90 &dictionary_list_dictionary);
91 DCHECK(success);
92 DCHECK(dictionary_list_dictionary);
94 return dictionary_list_dictionary;
97 // This function initializes a pref store with an empty version of the
98 // above schema, removing anything previously in the store under
99 // kPreferenceName.
100 void InitializePrefStore(WriteablePrefStore* store) {
101 scoped_ptr<base::DictionaryValue> empty_store(new base::DictionaryValue);
102 empty_store->SetInteger(kVersionKey, kVersion);
103 empty_store->Set(kDictionariesKey,
104 make_scoped_ptr(new base::DictionaryValue));
105 store->SetValue(kPreferenceName, empty_store.Pass(),
106 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
109 // A class to allow iteration over all dictionaries in the pref store, and
110 // easy lookup of the information associated with those dictionaries.
111 // Note that this is an "Iterator" in the same sense (and for the same
112 // reasons) that base::Dictionary::Iterator is an iterator--it allows
113 // iterating over all the dictionaries in the preference store, but it
114 // does not allow use as an STL iterator because the container it
115 // is iterating over does not export begin()/end() methods. This iterator can
116 // only be safely used on sanitized pref stores that are known to conform to the
117 // pref store schema.
118 class DictionaryPreferenceIterator {
119 public:
120 explicit DictionaryPreferenceIterator(WriteablePrefStore* pref_store);
122 bool IsAtEnd() const;
123 void Advance();
125 const std::string& server_hash() const { return server_hash_; }
126 const GURL& url() const { return url_; }
127 base::Time last_used() const { return last_used_; }
128 int use_count() const { return use_count_; }
129 int size() const { return size_; }
131 private:
132 void LoadDictionaryOrDie();
134 std::string server_hash_;
135 GURL url_;
136 base::Time last_used_;
137 int use_count_;
138 int size_;
140 base::DictionaryValue::Iterator dictionary_iterator_;
143 DictionaryPreferenceIterator::DictionaryPreferenceIterator(
144 WriteablePrefStore* pref_store)
145 : dictionary_iterator_(*GetPersistentStoreDictionaryMap(pref_store)) {
146 if (!IsAtEnd())
147 LoadDictionaryOrDie();
150 bool DictionaryPreferenceIterator::IsAtEnd() const {
151 return dictionary_iterator_.IsAtEnd();
154 void DictionaryPreferenceIterator::Advance() {
155 dictionary_iterator_.Advance();
156 if (!IsAtEnd())
157 LoadDictionaryOrDie();
160 void DictionaryPreferenceIterator::LoadDictionaryOrDie() {
161 double last_used_seconds_from_epoch;
162 const base::DictionaryValue* dict = nullptr;
163 bool success =
164 dictionary_iterator_.value().GetAsDictionary(&dict);
165 DCHECK(success);
167 server_hash_ = dictionary_iterator_.key();
169 std::string url_spec;
170 success = dict->GetString(kDictionaryUrlKey, &url_spec);
171 DCHECK(success);
172 url_ = GURL(url_spec);
174 success = dict->GetDouble(kDictionaryLastUsedKey,
175 &last_used_seconds_from_epoch);
176 DCHECK(success);
177 last_used_ = base::Time::FromDoubleT(last_used_seconds_from_epoch);
179 success = dict->GetInteger(kDictionaryUseCountKey, &use_count_);
180 DCHECK(success);
182 success = dict->GetInteger(kDictionarySizeKey, &size_);
183 DCHECK(success);
186 // Triggers a ReportValueChanged() on the specified WriteablePrefStore
187 // when the object goes out of scope.
188 class ScopedPrefNotifier {
189 public:
190 // Caller must guarantee lifetime of |*pref_store| exceeds the
191 // lifetime of this object.
192 ScopedPrefNotifier(WriteablePrefStore* pref_store)
193 : pref_store_(pref_store) {}
194 ~ScopedPrefNotifier() {
195 pref_store_->ReportValueChanged(
196 kPreferenceName, WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
199 private:
200 WriteablePrefStore* pref_store_;
202 DISALLOW_COPY_AND_ASSIGN(ScopedPrefNotifier);
205 } // namespace
207 // Adjust SDCH limits downwards for mobile.
208 #if defined(OS_ANDROID) || defined(OS_IOS)
209 // static
210 const size_t SdchOwner::kMaxTotalDictionarySize = 2 * 500 * 1000;
211 #else
212 // static
213 const size_t SdchOwner::kMaxTotalDictionarySize = 20 * 1000 * 1000;
214 #endif
216 // Somewhat arbitrary, but we assume a dictionary smaller than
217 // 50K isn't going to do anyone any good. Note that this still doesn't
218 // prevent download and addition unless there is less than this
219 // amount of space available in storage.
220 const size_t SdchOwner::kMinSpaceForDictionaryFetch = 50 * 1000;
222 void SdchOwner::RecordDictionaryFate(enum DictionaryFate fate) {
223 UMA_HISTOGRAM_ENUMERATION("Sdch3.DictionaryFate", fate, DICTIONARY_FATE_MAX);
226 void SdchOwner::RecordDictionaryEvictionOrUnload(const std::string& server_hash,
227 size_t size,
228 int use_count,
229 DictionaryFate fate) {
230 DCHECK(fate == DICTIONARY_FATE_EVICT_FOR_DICT ||
231 fate == DICTIONARY_FATE_EVICT_FOR_MEMORY ||
232 fate == DICTIONARY_FATE_EVICT_FOR_DESTRUCTION ||
233 fate == DICTIONARY_FATE_UNLOAD_FOR_DESTRUCTION);
235 UMA_HISTOGRAM_COUNTS_100("Sdch3.DictionaryUseCount", use_count);
236 RecordDictionaryFate(fate);
238 DCHECK(load_times_.count(server_hash) == 1);
239 base::Time now = clock_->Now();
240 base::TimeDelta dict_lifetime = now - load_times_[server_hash];
241 consumed_byte_seconds_.push_back(size * dict_lifetime.InMilliseconds());
242 load_times_.erase(server_hash);
245 SdchOwner::SdchOwner(SdchManager* sdch_manager, URLRequestContext* context)
246 : manager_(sdch_manager),
247 fetcher_(new SdchDictionaryFetcher(context)),
248 total_dictionary_bytes_(0),
249 clock_(new base::DefaultClock),
250 max_total_dictionary_size_(kMaxTotalDictionarySize),
251 min_space_for_dictionary_fetch_(kMinSpaceForDictionaryFetch),
252 memory_pressure_listener_(
253 base::Bind(&SdchOwner::OnMemoryPressure,
254 // Because |memory_pressure_listener_| is owned by
255 // SdchOwner, the SdchOwner object will be available
256 // for the lifetime of |memory_pressure_listener_|.
257 base::Unretained(this))),
258 in_memory_pref_store_(new ValueMapPrefStore()),
259 external_pref_store_(nullptr),
260 pref_store_(in_memory_pref_store_.get()),
261 creation_time_(clock_->Now()) {
262 manager_->AddObserver(this);
263 InitializePrefStore(pref_store_);
266 SdchOwner::~SdchOwner() {
267 for (DictionaryPreferenceIterator it(pref_store_); !it.IsAtEnd();
268 it.Advance()) {
269 int new_uses = it.use_count() - use_counts_at_load_[it.server_hash()];
270 DictionaryFate fate = IsPersistingDictionaries() ?
271 DICTIONARY_FATE_UNLOAD_FOR_DESTRUCTION :
272 DICTIONARY_FATE_EVICT_FOR_DESTRUCTION;
273 RecordDictionaryEvictionOrUnload(it.server_hash(), it.size(), new_uses,
274 fate);
276 manager_->RemoveObserver(this);
278 // This object only observes the external store during loading,
279 // i.e. before it's made the default preferences store.
280 if (external_pref_store_)
281 external_pref_store_->RemoveObserver(this);
283 int64 object_lifetime =
284 (clock_->Now() - creation_time_).InMilliseconds();
285 for (const auto& val : consumed_byte_seconds_) {
286 if (object_lifetime > 0) {
287 // Objects that are created and immediately destroyed don't add any memory
288 // pressure over time (and also cause a crash here).
289 UMA_HISTOGRAM_MEMORY_KB("Sdch3.TimeWeightedMemoryUse",
290 val / object_lifetime);
296 void SdchOwner::EnablePersistentStorage(PersistentPrefStore* pref_store) {
297 DCHECK(!external_pref_store_);
298 external_pref_store_ = pref_store;
299 external_pref_store_->AddObserver(this);
301 if (external_pref_store_->IsInitializationComplete())
302 OnInitializationCompleted(true);
305 void SdchOwner::SetMaxTotalDictionarySize(size_t max_total_dictionary_size) {
306 max_total_dictionary_size_ = max_total_dictionary_size;
309 void SdchOwner::SetMinSpaceForDictionaryFetch(
310 size_t min_space_for_dictionary_fetch) {
311 min_space_for_dictionary_fetch_ = min_space_for_dictionary_fetch;
314 void SdchOwner::OnDictionaryFetched(base::Time last_used,
315 int use_count,
316 const std::string& dictionary_text,
317 const GURL& dictionary_url,
318 const BoundNetLog& net_log,
319 bool was_from_cache) {
320 struct DictionaryItem {
321 base::Time last_used;
322 std::string server_hash;
323 int use_count;
324 size_t dictionary_size;
326 DictionaryItem() : use_count(0), dictionary_size(0) {}
327 DictionaryItem(const base::Time& last_used,
328 const std::string& server_hash,
329 int use_count,
330 size_t dictionary_size)
331 : last_used(last_used),
332 server_hash(server_hash),
333 use_count(use_count),
334 dictionary_size(dictionary_size) {}
335 DictionaryItem(const DictionaryItem& rhs) = default;
336 DictionaryItem& operator=(const DictionaryItem& rhs) = default;
337 bool operator<(const DictionaryItem& rhs) const {
338 return last_used < rhs.last_used;
342 if (!was_from_cache)
343 UMA_HISTOGRAM_COUNTS("Sdch3.NetworkBytesSpent", dictionary_text.size());
345 // Figure out if there is space for the incoming dictionary; evict
346 // stale dictionaries if needed to make space.
348 std::vector<DictionaryItem> stale_dictionary_list;
349 size_t recoverable_bytes = 0;
350 base::Time now(clock_->Now());
351 // Dictionaries whose last used time is before |stale_boundary| are candidates
352 // for eviction if necessary.
353 base::Time stale_boundary(
354 now - base::TimeDelta::FromHours(kFreshnessLifetimeHours));
355 // Dictionaries that have never been used and are from before
356 // |never_used_stale_boundary| are candidates for eviction if necessary.
357 base::Time never_used_stale_boundary(
358 now - base::TimeDelta::FromHours(kNeverUsedFreshnessLifetimeHours));
359 for (DictionaryPreferenceIterator it(pref_store_); !it.IsAtEnd();
360 it.Advance()) {
361 if (it.last_used() < stale_boundary ||
362 (it.use_count() == 0 && it.last_used() < never_used_stale_boundary)) {
363 stale_dictionary_list.push_back(DictionaryItem(
364 it.last_used(), it.server_hash(), it.use_count(), it.size()));
365 recoverable_bytes += it.size();
369 if (total_dictionary_bytes_ + dictionary_text.size() - recoverable_bytes >
370 max_total_dictionary_size_) {
371 RecordDictionaryFate(DICTIONARY_FATE_FETCH_IGNORED_NO_SPACE);
372 SdchManager::SdchErrorRecovery(SDCH_DICTIONARY_NO_ROOM);
373 net_log.AddEvent(NetLog::TYPE_SDCH_DICTIONARY_ERROR,
374 base::Bind(&NetLogSdchDictionaryFetchProblemCallback,
375 SDCH_DICTIONARY_NO_ROOM, dictionary_url, true));
376 return;
379 // Add the new dictionary. This is done before removing the stale
380 // dictionaries so that no state change will occur if dictionary addition
381 // fails.
382 std::string server_hash;
383 SdchProblemCode rv = manager_->AddSdchDictionary(
384 dictionary_text, dictionary_url, &server_hash);
385 if (rv != SDCH_OK) {
386 RecordDictionaryFate(DICTIONARY_FATE_FETCH_MANAGER_REFUSED);
387 SdchManager::SdchErrorRecovery(rv);
388 net_log.AddEvent(NetLog::TYPE_SDCH_DICTIONARY_ERROR,
389 base::Bind(&NetLogSdchDictionaryFetchProblemCallback, rv,
390 dictionary_url, true));
391 return;
394 base::DictionaryValue* pref_dictionary_map =
395 GetPersistentStoreDictionaryMap(pref_store_);
396 ScopedPrefNotifier scoped_pref_notifier(pref_store_);
398 // Remove the old dictionaries.
399 std::sort(stale_dictionary_list.begin(), stale_dictionary_list.end());
400 size_t avail_bytes = max_total_dictionary_size_ - total_dictionary_bytes_;
401 auto stale_it = stale_dictionary_list.begin();
402 while (avail_bytes < dictionary_text.size() &&
403 stale_it != stale_dictionary_list.end()) {
404 manager_->RemoveSdchDictionary(stale_it->server_hash);
406 DCHECK(pref_dictionary_map->HasKey(stale_it->server_hash));
407 bool success = pref_dictionary_map->RemoveWithoutPathExpansion(
408 stale_it->server_hash, nullptr);
409 DCHECK(success);
411 avail_bytes += stale_it->dictionary_size;
413 int new_uses = stale_it->use_count -
414 use_counts_at_load_[stale_it->server_hash];
415 RecordDictionaryEvictionOrUnload(stale_it->server_hash,
416 stale_it->dictionary_size,
417 new_uses,
418 DICTIONARY_FATE_EVICT_FOR_DICT);
420 ++stale_it;
422 DCHECK_GE(avail_bytes, dictionary_text.size());
424 RecordDictionaryFate(
425 // Distinguish between loads triggered by network responses and
426 // loads triggered by persistence.
427 last_used.is_null() ? DICTIONARY_FATE_ADD_RESPONSE_TRIGGERED
428 : DICTIONARY_FATE_ADD_PERSISTENCE_TRIGGERED);
430 // If a dictionary has never been used, its dictionary addition time
431 // is recorded as its last used time. Never used dictionaries are treated
432 // specially in the freshness logic.
433 if (last_used.is_null())
434 last_used = clock_->Now();
436 total_dictionary_bytes_ += dictionary_text.size();
438 // Record the addition in the pref store.
439 scoped_ptr<base::DictionaryValue> dictionary_description(
440 new base::DictionaryValue());
441 dictionary_description->SetString(kDictionaryUrlKey, dictionary_url.spec());
442 dictionary_description->SetDouble(kDictionaryLastUsedKey,
443 last_used.ToDoubleT());
444 dictionary_description->SetInteger(kDictionaryUseCountKey, use_count);
445 dictionary_description->SetInteger(kDictionarySizeKey,
446 dictionary_text.size());
447 pref_dictionary_map->Set(server_hash, dictionary_description.Pass());
448 load_times_[server_hash] = clock_->Now();
451 void SdchOwner::OnDictionaryAdded(const GURL& dictionary_url,
452 const std::string& server_hash) { }
454 void SdchOwner::OnDictionaryRemoved(const std::string& server_hash) { }
456 void SdchOwner::OnDictionaryUsed(const std::string& server_hash) {
457 base::Time now(clock_->Now());
458 base::DictionaryValue* pref_dictionary_map =
459 GetPersistentStoreDictionaryMap(pref_store_);
460 ScopedPrefNotifier scoped_pref_notifier(pref_store_);
462 base::Value* value = nullptr;
463 bool success = pref_dictionary_map->Get(server_hash, &value);
464 if (!success) {
465 // SdchManager::GetDictionarySet() pins the referenced dictionaries in
466 // memory past a possible deletion. For this reason, OnDictionaryUsed()
467 // notifications may occur after SdchOwner thinks that dictionaries
468 // have been deleted.
469 SdchManager::SdchErrorRecovery(SDCH_DICTIONARY_USED_AFTER_DELETION);
470 return;
472 base::DictionaryValue* specific_dictionary_map = nullptr;
473 success = value->GetAsDictionary(&specific_dictionary_map);
474 DCHECK(success);
476 double last_used_seconds_since_epoch = 0.0;
477 success = specific_dictionary_map->GetDouble(kDictionaryLastUsedKey,
478 &last_used_seconds_since_epoch);
479 DCHECK(success);
480 int use_count = 0;
481 success =
482 specific_dictionary_map->GetInteger(kDictionaryUseCountKey, &use_count);
483 DCHECK(success);
485 if (use_counts_at_load_.count(server_hash) == 0) {
486 use_counts_at_load_[server_hash] = use_count;
489 base::TimeDelta time_since_last_used(now -
490 base::Time::FromDoubleT(last_used_seconds_since_epoch));
492 // TODO(rdsmith): Distinguish between "Never used" and "Actually not
493 // touched for 48 hours".
494 UMA_HISTOGRAM_CUSTOM_TIMES(
495 "Sdch3.UsageInterval",
496 use_count ? time_since_last_used : base::TimeDelta::FromHours(48),
497 base::TimeDelta(), base::TimeDelta::FromHours(48), 50);
499 specific_dictionary_map->SetDouble(kDictionaryLastUsedKey, now.ToDoubleT());
500 specific_dictionary_map->SetInteger(kDictionaryUseCountKey, use_count + 1);
503 void SdchOwner::OnGetDictionary(const GURL& request_url,
504 const GURL& dictionary_url) {
505 base::Time stale_boundary(clock_->Now() - base::TimeDelta::FromDays(1));
506 size_t avail_bytes = 0;
507 for (DictionaryPreferenceIterator it(pref_store_); !it.IsAtEnd();
508 it.Advance()) {
509 if (it.last_used() < stale_boundary)
510 avail_bytes += it.size();
513 // Don't initiate the fetch if we wouldn't be able to store any
514 // reasonable dictionary.
515 // TODO(rdsmith): Maybe do a HEAD request to figure out how much
516 // storage we'd actually need?
517 if (max_total_dictionary_size_ < (total_dictionary_bytes_ - avail_bytes +
518 min_space_for_dictionary_fetch_)) {
519 RecordDictionaryFate(DICTIONARY_FATE_GET_IGNORED);
520 // TODO(rdsmith): Log a net-internals error. This requires
521 // SdchManager to forward the URLRequest that detected the
522 // Get-Dictionary header to its observers, which is tricky
523 // because SdchManager is layered underneath URLRequest.
524 return;
527 fetcher_->Schedule(dictionary_url,
528 base::Bind(&SdchOwner::OnDictionaryFetched,
529 // SdchOwner will outlive its member variables.
530 base::Unretained(this), base::Time(), 0));
533 void SdchOwner::OnClearDictionaries() {
534 total_dictionary_bytes_ = 0;
535 fetcher_->Cancel();
537 InitializePrefStore(pref_store_);
540 void SdchOwner::OnPrefValueChanged(const std::string& key) {
543 void SdchOwner::OnInitializationCompleted(bool succeeded) {
544 PersistentPrefStore::PrefReadError error =
545 external_pref_store_->GetReadError();
546 // Errors on load are self-correcting; if dictionaries were not
547 // persisted from the last instance of the browser, they will be
548 // faulted in by user action over time. However, if a load error
549 // means that the dictionary information won't be able to be persisted,
550 // the in memory pref store is left in place.
551 if (!succeeded) {
552 // Failure means a write failed, since read failures are recoverable.
553 DCHECK_NE(
554 error,
555 PersistentPrefStore::PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE);
556 DCHECK_NE(error,
557 PersistentPrefStore::PREF_READ_ERROR_MAX_ENUM);
559 LOG(ERROR) << "Pref store write failed: " << error;
560 external_pref_store_->RemoveObserver(this);
561 external_pref_store_ = nullptr;
562 RecordPersistenceFailure(PERSISTENCE_FAILURE_REASON_WRITE_FAILED);
563 return;
565 switch (external_pref_store_->GetReadError()) {
566 case PersistentPrefStore::PREF_READ_ERROR_NONE:
567 break;
569 case PersistentPrefStore::PREF_READ_ERROR_NO_FILE:
570 // First time reading; the file will be created.
571 RecordPersistenceFailure(PERSISTENCE_FAILURE_REASON_NO_FILE);
572 break;
574 case PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE:
575 case PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE:
576 case PersistentPrefStore::PREF_READ_ERROR_FILE_OTHER:
577 case PersistentPrefStore::PREF_READ_ERROR_FILE_LOCKED:
578 case PersistentPrefStore::PREF_READ_ERROR_JSON_REPEAT:
579 RecordPersistenceFailure(PERSISTENCE_FAILURE_REASON_READ_FAILED);
580 break;
582 case PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED:
583 case PersistentPrefStore::PREF_READ_ERROR_FILE_NOT_SPECIFIED:
584 case PersistentPrefStore::PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE:
585 case PersistentPrefStore::PREF_READ_ERROR_MAX_ENUM:
586 // Shouldn't ever happen. ACCESS_DENIED and FILE_NOT_SPECIFIED should
587 // imply !succeeded, and TASK_INCOMPLETE should never be delivered.
588 NOTREACHED();
589 break;
593 // Load in what was stored before chrome exited previously.
594 const base::Value* sdch_persistence_value = nullptr;
595 const base::DictionaryValue* sdch_persistence_dictionary = nullptr;
597 // The GetPersistentStore() routine above assumes data formatted
598 // according to the schema described at the top of this file. Since
599 // this data comes from disk, to avoid disk corruption resulting in
600 // persistent chrome errors this code avoids those assupmtions.
601 if (external_pref_store_->GetValue(kPreferenceName,
602 &sdch_persistence_value) &&
603 sdch_persistence_value->GetAsDictionary(&sdch_persistence_dictionary)) {
604 SchedulePersistedDictionaryLoads(*sdch_persistence_dictionary);
607 // Reset the persistent store and update it with the accumulated
608 // information from the local store.
609 InitializePrefStore(external_pref_store_);
611 ScopedPrefNotifier scoped_pref_notifier(external_pref_store_);
612 GetPersistentStoreDictionaryMap(external_pref_store_)
613 ->Swap(GetPersistentStoreDictionaryMap(in_memory_pref_store_.get()));
615 // This object can stop waiting on (i.e. observing) the external preference
616 // store and switch over to using it as the primary preference store.
617 pref_store_ = external_pref_store_;
618 external_pref_store_->RemoveObserver(this);
619 external_pref_store_ = nullptr;
620 in_memory_pref_store_ = nullptr;
623 void SdchOwner::SetClockForTesting(scoped_ptr<base::Clock> clock) {
624 clock_ = clock.Pass();
627 int SdchOwner::GetDictionaryCountForTesting() const {
628 int count = 0;
629 for (DictionaryPreferenceIterator it(pref_store_); !it.IsAtEnd();
630 it.Advance()) {
631 count++;
633 return count;
636 bool SdchOwner::HasDictionaryFromURLForTesting(const GURL& url) const {
637 for (DictionaryPreferenceIterator it(pref_store_); !it.IsAtEnd();
638 it.Advance()) {
639 if (it.url() == url)
640 return true;
642 return false;
645 void SdchOwner::SetFetcherForTesting(
646 scoped_ptr<SdchDictionaryFetcher> fetcher) {
647 fetcher_.reset(fetcher.release());
650 void SdchOwner::OnMemoryPressure(
651 base::MemoryPressureListener::MemoryPressureLevel level) {
652 DCHECK_NE(base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, level);
654 for (DictionaryPreferenceIterator it(pref_store_); !it.IsAtEnd();
655 it.Advance()) {
656 int new_uses = it.use_count() - use_counts_at_load_[it.server_hash()];
657 RecordDictionaryEvictionOrUnload(it.server_hash(),
658 it.size(),
659 new_uses,
660 DICTIONARY_FATE_EVICT_FOR_MEMORY);
663 // TODO(rdsmith): Make a distinction between moderate and critical
664 // memory pressure.
665 manager_->ClearData();
668 bool SdchOwner::SchedulePersistedDictionaryLoads(
669 const base::DictionaryValue& persisted_info) {
670 // Any schema error will result in dropping the persisted info.
671 int version = 0;
672 if (!persisted_info.GetInteger(kVersionKey, &version))
673 return false;
675 // Any version mismatch will result in dropping the persisted info;
676 // it will be faulted in at small performance cost as URLs using
677 // dictionaries for encoding are visited.
678 if (version != kVersion)
679 return false;
681 const base::DictionaryValue* dictionary_set = nullptr;
682 if (!persisted_info.GetDictionary(kDictionariesKey, &dictionary_set))
683 return false;
685 // Any formatting error will result in skipping that particular
686 // dictionary.
687 for (base::DictionaryValue::Iterator dict_it(*dictionary_set);
688 !dict_it.IsAtEnd(); dict_it.Advance()) {
689 const base::DictionaryValue* dict_info = nullptr;
690 if (!dict_it.value().GetAsDictionary(&dict_info))
691 continue;
693 std::string url_string;
694 if (!dict_info->GetString(kDictionaryUrlKey, &url_string))
695 continue;
696 GURL dict_url(url_string);
698 double last_used;
699 if (!dict_info->GetDouble(kDictionaryLastUsedKey, &last_used))
700 continue;
702 int use_count;
703 if (!dict_info->GetInteger(kDictionaryUseCountKey, &use_count))
704 continue;
706 fetcher_->ScheduleReload(
707 dict_url, base::Bind(&SdchOwner::OnDictionaryFetched,
708 // SdchOwner will outlive its member variables.
709 base::Unretained(this),
710 base::Time::FromDoubleT(last_used),
711 use_count));
714 return true;
717 bool SdchOwner::IsPersistingDictionaries() const {
718 return in_memory_pref_store_.get() != nullptr;
721 } // namespace net