Check USB device path access when prompting users to select a device.
[chromium-blink-merge.git] / net / sdch / sdch_owner.cc
blob948ee1f08123d606264bd2eb877d6a014348c9ba
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 DictionaryFate {
24 // A Get-Dictionary header wasn't acted on.
25 DICTIONARY_FATE_GET_IGNORED = 1,
27 // A fetch was attempted, but failed.
28 // TODO(rdsmith): Actually record this case.
29 DICTIONARY_FATE_FETCH_FAILED = 2,
31 // A successful fetch was dropped on the floor, no space.
32 DICTIONARY_FATE_FETCH_IGNORED_NO_SPACE = 3,
34 // A successful fetch was refused by the SdchManager.
35 DICTIONARY_FATE_FETCH_MANAGER_REFUSED = 4,
37 // A dictionary was successfully added based on
38 // a Get-Dictionary header in a response.
39 DICTIONARY_FATE_ADD_RESPONSE_TRIGGERED = 5,
41 // A dictionary was evicted by an incoming dict.
42 DICTIONARY_FATE_EVICT_FOR_DICT = 6,
44 // A dictionary was evicted by memory pressure.
45 DICTIONARY_FATE_EVICT_FOR_MEMORY = 7,
47 // A dictionary was evicted on destruction.
48 DICTIONARY_FATE_EVICT_FOR_DESTRUCTION = 8,
50 // A dictionary was successfully added based on
51 // persistence from a previous browser revision.
52 DICTIONARY_FATE_ADD_PERSISTENCE_TRIGGERED = 9,
54 // A dictionary was unloaded on destruction, but is still present on disk.
55 DICTIONARY_FATE_UNLOAD_FOR_DESTRUCTION = 10,
57 DICTIONARY_FATE_MAX = 11
60 enum PersistenceFailureReason {
61 // File didn't exist; is being created.
62 PERSISTENCE_FAILURE_REASON_NO_FILE = 1,
64 // Error reading in information, but should be able to write.
65 PERSISTENCE_FAILURE_REASON_READ_FAILED = 2,
67 // Error leading to abort on attempted persistence.
68 PERSISTENCE_FAILURE_REASON_WRITE_FAILED = 3,
70 PERSISTENCE_FAILURE_REASON_MAX = 4
73 // Dictionaries that haven't been touched in 24 hours may be evicted
74 // to make room for new dictionaries.
75 const int kFreshnessLifetimeHours = 24;
77 // Dictionaries that have never been used only stay fresh for one hour.
78 const int kNeverUsedFreshnessLifetimeHours = 1;
80 void RecordDictionaryFate(enum DictionaryFate fate) {
81 UMA_HISTOGRAM_ENUMERATION("Sdch3.DictionaryFate", fate, DICTIONARY_FATE_MAX);
84 void RecordPersistenceFailure(PersistenceFailureReason failure_reason) {
85 UMA_HISTOGRAM_ENUMERATION("Sdch3.PersistenceFailureReason", failure_reason,
86 PERSISTENCE_FAILURE_REASON_MAX);
89 void RecordDictionaryEvictionOrUnload(int use_count, DictionaryFate fate) {
90 DCHECK(fate == DICTIONARY_FATE_EVICT_FOR_DICT ||
91 fate == DICTIONARY_FATE_EVICT_FOR_MEMORY ||
92 fate == DICTIONARY_FATE_EVICT_FOR_DESTRUCTION ||
93 fate == DICTIONARY_FATE_UNLOAD_FOR_DESTRUCTION);
95 UMA_HISTOGRAM_COUNTS_100("Sdch3.DictionaryUseCount", use_count);
96 RecordDictionaryFate(fate);
99 // Schema specifications and access routines.
101 // The persistent prefs store is conceptually shared with any other network
102 // stack systems that want to persist data over browser restarts, and so
103 // use of it must be namespace restricted.
104 // Schema:
105 // pref_store_->GetValue(kPreferenceName) -> Dictionary {
106 // 'version' -> 1 [int]
107 // 'dictionaries' -> Dictionary {
108 // server_hash -> {
109 // 'url' -> URL [string]
110 // 'last_used' -> seconds since unix epoch [double]
111 // 'use_count' -> use count [int]
112 // 'size' -> size [int]
113 // }
114 // }
115 const char kPreferenceName[] = "SDCH";
116 const char kVersionKey[] = "version";
117 const char kDictionariesKey[] = "dictionaries";
118 const char kDictionaryUrlKey[] = "url";
119 const char kDictionaryLastUsedKey[] = "last_used";
120 const char kDictionaryUseCountKey[] = "use_count";
121 const char kDictionarySizeKey[] = "size";
123 const int kVersion = 1;
125 // This function returns store[kPreferenceName/kDictionariesKey]. The caller
126 // is responsible for making sure any needed calls to
127 // |store->ReportValueChanged()| occur.
128 base::DictionaryValue* GetPersistentStoreDictionaryMap(
129 WriteablePrefStore* store) {
130 base::Value* result = nullptr;
131 bool success = store->GetMutableValue(kPreferenceName, &result);
132 DCHECK(success);
134 base::DictionaryValue* preference_dictionary = nullptr;
135 success = result->GetAsDictionary(&preference_dictionary);
136 DCHECK(success);
137 DCHECK(preference_dictionary);
139 base::DictionaryValue* dictionary_list_dictionary = nullptr;
140 success = preference_dictionary->GetDictionary(kDictionariesKey,
141 &dictionary_list_dictionary);
142 DCHECK(success);
143 DCHECK(dictionary_list_dictionary);
145 return dictionary_list_dictionary;
148 // This function initializes a pref store with an empty version of the
149 // above schema, removing anything previously in the store under
150 // kPreferenceName.
151 void InitializePrefStore(WriteablePrefStore* store) {
152 base::DictionaryValue* empty_store(new base::DictionaryValue);
153 empty_store->SetInteger(kVersionKey, kVersion);
154 empty_store->Set(kDictionariesKey,
155 make_scoped_ptr(new base::DictionaryValue));
156 store->SetValue(kPreferenceName, empty_store);
159 // A class to allow iteration over all dictionaries in the pref store, and
160 // easy lookup of the information associated with those dictionaries.
161 // Note that this is an "Iterator" in the same sense (and for the same
162 // reasons) that base::Dictionary::Iterator is an iterator--it allows
163 // iterating over all the dictionaries in the preference store, but it
164 // does not allow use as an STL iterator because the container it
165 // is iterating over does not export begin()/end() methods. This iterator can
166 // only be safely used on sanitized pref stores that are known to conform to the
167 // pref store schema.
168 class DictionaryPreferenceIterator {
169 public:
170 explicit DictionaryPreferenceIterator(WriteablePrefStore* pref_store);
172 bool IsAtEnd() const;
173 void Advance();
175 const std::string& server_hash() const { return server_hash_; }
176 const GURL url() const { return url_; }
177 base::Time last_used() const { return last_used_; }
178 int use_count() const { return use_count_; }
179 int size() const { return size_; }
181 private:
182 void LoadDictionaryOrDie();
184 std::string server_hash_;
185 GURL url_;
186 base::Time last_used_;
187 int use_count_;
188 int size_;
190 base::DictionaryValue::Iterator dictionary_iterator_;
193 DictionaryPreferenceIterator::DictionaryPreferenceIterator(
194 WriteablePrefStore* pref_store)
195 : dictionary_iterator_(*GetPersistentStoreDictionaryMap(pref_store)) {
196 if (!IsAtEnd())
197 LoadDictionaryOrDie();
200 bool DictionaryPreferenceIterator::IsAtEnd() const {
201 return dictionary_iterator_.IsAtEnd();
204 void DictionaryPreferenceIterator::Advance() {
205 dictionary_iterator_.Advance();
206 if (!IsAtEnd())
207 LoadDictionaryOrDie();
210 void DictionaryPreferenceIterator::LoadDictionaryOrDie() {
211 double last_used_seconds_from_epoch;
212 const base::DictionaryValue* dict = nullptr;
213 bool success =
214 dictionary_iterator_.value().GetAsDictionary(&dict);
215 DCHECK(success);
217 server_hash_ = dictionary_iterator_.key();
219 std::string url_spec;
220 success = dict->GetString(kDictionaryUrlKey, &url_spec);
221 DCHECK(success);
222 url_ = GURL(url_spec);
224 success = dict->GetDouble(kDictionaryLastUsedKey,
225 &last_used_seconds_from_epoch);
226 DCHECK(success);
227 last_used_ = base::Time::FromDoubleT(last_used_seconds_from_epoch);
229 success = dict->GetInteger(kDictionaryUseCountKey, &use_count_);
230 DCHECK(success);
232 success = dict->GetInteger(kDictionarySizeKey, &size_);
233 DCHECK(success);
236 // Triggers a ReportValueChanged() on the specified WriteablePrefStore
237 // when the object goes out of scope.
238 class ScopedPrefNotifier {
239 public:
240 // Caller must guarantee lifetime of |*pref_store| exceeds the
241 // lifetime of this object.
242 ScopedPrefNotifier(WriteablePrefStore* pref_store)
243 : pref_store_(pref_store) {}
244 ~ScopedPrefNotifier() { pref_store_->ReportValueChanged(kPreferenceName); }
246 private:
247 WriteablePrefStore* pref_store_;
249 DISALLOW_COPY_AND_ASSIGN(ScopedPrefNotifier);
252 } // namespace
254 // Adjust SDCH limits downwards for mobile.
255 #if defined(OS_ANDROID) || defined(OS_IOS)
256 // static
257 const size_t SdchOwner::kMaxTotalDictionarySize = 2 * 500 * 1000;
258 #else
259 // static
260 const size_t SdchOwner::kMaxTotalDictionarySize = 20 * 1000 * 1000;
261 #endif
263 // Somewhat arbitrary, but we assume a dictionary smaller than
264 // 50K isn't going to do anyone any good. Note that this still doesn't
265 // prevent download and addition unless there is less than this
266 // amount of space available in storage.
267 const size_t SdchOwner::kMinSpaceForDictionaryFetch = 50 * 1000;
269 SdchOwner::SdchOwner(SdchManager* sdch_manager, URLRequestContext* context)
270 : manager_(sdch_manager->GetWeakPtr()),
271 fetcher_(new SdchDictionaryFetcher(context)),
272 total_dictionary_bytes_(0),
273 clock_(new base::DefaultClock),
274 max_total_dictionary_size_(kMaxTotalDictionarySize),
275 min_space_for_dictionary_fetch_(kMinSpaceForDictionaryFetch),
276 #if defined(OS_CHROMEOS)
277 // For debugging http://crbug.com/454198; remove when resolved.
278 destroyed_(0),
279 #endif
280 memory_pressure_listener_(
281 base::Bind(&SdchOwner::OnMemoryPressure,
282 // Because |memory_pressure_listener_| is owned by
283 // SdchOwner, the SdchOwner object will be available
284 // for the lifetime of |memory_pressure_listener_|.
285 base::Unretained(this))),
286 in_memory_pref_store_(new ValueMapPrefStore()),
287 external_pref_store_(nullptr),
288 pref_store_(in_memory_pref_store_.get()) {
289 #if defined(OS_CHROMEOS)
290 // For debugging http://crbug.com/454198; remove when resolved.
291 CHECK(clock_.get());
292 #endif
293 manager_->AddObserver(this);
294 InitializePrefStore(pref_store_);
297 SdchOwner::~SdchOwner() {
298 #if defined(OS_CHROMEOS)
299 // For debugging http://crbug.com/454198; remove when resolved.
300 CHECK_EQ(0u, destroyed_);
301 CHECK(clock_.get());
302 clock_.reset();
303 CHECK(manager_.get());
304 #endif
306 for (DictionaryPreferenceIterator it(pref_store_); !it.IsAtEnd();
307 it.Advance()) {
308 int new_uses = it.use_count() - use_counts_at_load_[it.server_hash()];
309 DictionaryFate fate = IsPersistingDictionaries() ?
310 DICTIONARY_FATE_UNLOAD_FOR_DESTRUCTION :
311 DICTIONARY_FATE_EVICT_FOR_DESTRUCTION;
312 RecordDictionaryEvictionOrUnload(new_uses, fate);
314 manager_->RemoveObserver(this);
316 // This object only observes the external store during loading,
317 // i.e. before it's made the default preferences store.
318 if (external_pref_store_)
319 external_pref_store_->RemoveObserver(this);
321 #if defined(OS_CHROMEOS)
322 destroyed_ = 0xdeadbeef;
323 #endif
326 void SdchOwner::EnablePersistentStorage(PersistentPrefStore* pref_store) {
327 DCHECK(!external_pref_store_);
328 external_pref_store_ = pref_store;
329 external_pref_store_->AddObserver(this);
331 if (external_pref_store_->IsInitializationComplete())
332 OnInitializationCompleted(true);
335 void SdchOwner::SetMaxTotalDictionarySize(size_t max_total_dictionary_size) {
336 max_total_dictionary_size_ = max_total_dictionary_size;
339 void SdchOwner::SetMinSpaceForDictionaryFetch(
340 size_t min_space_for_dictionary_fetch) {
341 min_space_for_dictionary_fetch_ = min_space_for_dictionary_fetch;
344 void SdchOwner::OnDictionaryFetched(base::Time last_used,
345 int use_count,
346 const std::string& dictionary_text,
347 const GURL& dictionary_url,
348 const net::BoundNetLog& net_log,
349 bool was_from_cache) {
350 struct DictionaryItem {
351 base::Time last_used;
352 std::string server_hash;
353 int use_count;
354 size_t dictionary_size;
356 DictionaryItem() : use_count(0), dictionary_size(0) {}
357 DictionaryItem(const base::Time& last_used,
358 const std::string& server_hash,
359 int use_count,
360 size_t dictionary_size)
361 : last_used(last_used),
362 server_hash(server_hash),
363 use_count(use_count),
364 dictionary_size(dictionary_size) {}
365 DictionaryItem(const DictionaryItem& rhs) = default;
366 DictionaryItem& operator=(const DictionaryItem& rhs) = default;
367 bool operator<(const DictionaryItem& rhs) const {
368 return last_used < rhs.last_used;
372 #if defined(OS_CHROMEOS)
373 // For debugging http://crbug.com/454198; remove when resolved.
374 CHECK_EQ(0u, destroyed_);
375 CHECK(clock_.get());
376 #endif
378 if (!was_from_cache)
379 UMA_HISTOGRAM_COUNTS("Sdch3.NetworkBytesSpent", dictionary_text.size());
381 // Figure out if there is space for the incoming dictionary; evict
382 // stale dictionaries if needed to make space.
384 std::vector<DictionaryItem> stale_dictionary_list;
385 size_t recoverable_bytes = 0;
386 base::Time now(clock_->Now());
387 // Dictionaries whose last used time is before |stale_boundary| are candidates
388 // for eviction if necessary.
389 base::Time stale_boundary(
390 now - base::TimeDelta::FromHours(kFreshnessLifetimeHours));
391 // Dictionaries that have never been used and are from before
392 // |never_used_stale_boundary| are candidates for eviction if necessary.
393 base::Time never_used_stale_boundary(
394 now - base::TimeDelta::FromHours(kNeverUsedFreshnessLifetimeHours));
395 for (DictionaryPreferenceIterator it(pref_store_); !it.IsAtEnd();
396 it.Advance()) {
397 if (it.last_used() < stale_boundary ||
398 (it.use_count() == 0 && it.last_used() < never_used_stale_boundary)) {
399 stale_dictionary_list.push_back(DictionaryItem(
400 it.last_used(), it.server_hash(), it.use_count(), it.size()));
401 recoverable_bytes += it.size();
405 #if defined(OS_CHROMEOS)
406 // For debugging http://crbug.com/454198; remove when resolved.
407 CHECK_EQ(0u, destroyed_);
408 CHECK(clock_.get());
409 #endif
411 if (total_dictionary_bytes_ + dictionary_text.size() - recoverable_bytes >
412 max_total_dictionary_size_) {
413 RecordDictionaryFate(DICTIONARY_FATE_FETCH_IGNORED_NO_SPACE);
414 net::SdchManager::SdchErrorRecovery(SDCH_DICTIONARY_NO_ROOM);
415 net_log.AddEvent(net::NetLog::TYPE_SDCH_DICTIONARY_ERROR,
416 base::Bind(&net::NetLogSdchDictionaryFetchProblemCallback,
417 SDCH_DICTIONARY_NO_ROOM, dictionary_url, true));
418 return;
421 // Add the new dictionary. This is done before removing the stale
422 // dictionaries so that no state change will occur if dictionary addition
423 // fails.
424 std::string server_hash;
425 net::SdchProblemCode rv = manager_->AddSdchDictionary(
426 dictionary_text, dictionary_url, &server_hash);
427 if (rv != net::SDCH_OK) {
428 RecordDictionaryFate(DICTIONARY_FATE_FETCH_MANAGER_REFUSED);
429 net::SdchManager::SdchErrorRecovery(rv);
430 net_log.AddEvent(net::NetLog::TYPE_SDCH_DICTIONARY_ERROR,
431 base::Bind(&net::NetLogSdchDictionaryFetchProblemCallback,
432 rv, dictionary_url, true));
433 return;
436 base::DictionaryValue* pref_dictionary_map =
437 GetPersistentStoreDictionaryMap(pref_store_);
438 ScopedPrefNotifier scoped_pref_notifier(pref_store_);
440 // Remove the old dictionaries.
441 std::sort(stale_dictionary_list.begin(), stale_dictionary_list.end());
442 size_t avail_bytes = max_total_dictionary_size_ - total_dictionary_bytes_;
443 auto stale_it = stale_dictionary_list.begin();
444 while (avail_bytes < dictionary_text.size() &&
445 stale_it != stale_dictionary_list.end()) {
446 manager_->RemoveSdchDictionary(stale_it->server_hash);
448 DCHECK(pref_dictionary_map->HasKey(stale_it->server_hash));
449 bool success = pref_dictionary_map->RemoveWithoutPathExpansion(
450 stale_it->server_hash, nullptr);
451 DCHECK(success);
453 avail_bytes += stale_it->dictionary_size;
455 int new_uses = stale_it->use_count -
456 use_counts_at_load_[stale_it->server_hash];
457 RecordDictionaryEvictionOrUnload(new_uses,
458 DICTIONARY_FATE_EVICT_FOR_DICT);
460 ++stale_it;
462 DCHECK_GE(avail_bytes, dictionary_text.size());
464 RecordDictionaryFate(
465 // Distinguish between loads triggered by network responses and
466 // loads triggered by persistence.
467 last_used.is_null() ? DICTIONARY_FATE_ADD_RESPONSE_TRIGGERED
468 : DICTIONARY_FATE_ADD_PERSISTENCE_TRIGGERED);
470 // If a dictionary has never been used, its dictionary addition time
471 // is recorded as its last used time. Never used dictionaries are treated
472 // specially in the freshness logic.
473 if (last_used.is_null())
474 last_used = clock_->Now();
476 total_dictionary_bytes_ += dictionary_text.size();
478 #if defined(OS_CHROMEOS)
479 // For debugging http://crbug.com/454198; remove when resolved.
480 CHECK_EQ(0u, destroyed_);
481 CHECK(clock_.get());
482 #endif
484 // Record the addition in the pref store.
485 scoped_ptr<base::DictionaryValue> dictionary_description(
486 new base::DictionaryValue());
487 dictionary_description->SetString(kDictionaryUrlKey, dictionary_url.spec());
488 dictionary_description->SetDouble(kDictionaryLastUsedKey,
489 last_used.ToDoubleT());
490 dictionary_description->SetInteger(kDictionaryUseCountKey, use_count);
491 dictionary_description->SetInteger(kDictionarySizeKey,
492 dictionary_text.size());
493 pref_dictionary_map->Set(server_hash, dictionary_description.Pass());
496 void SdchOwner::OnDictionaryUsed(SdchManager* manager,
497 const std::string& server_hash) {
498 base::Time now(clock_->Now());
499 base::DictionaryValue* pref_dictionary_map =
500 GetPersistentStoreDictionaryMap(pref_store_);
501 ScopedPrefNotifier scoped_pref_notifier(pref_store_);
503 base::Value* value = nullptr;
504 bool success = pref_dictionary_map->Get(server_hash, &value);
505 if (!success) {
506 // SdchManager::GetDictionarySet() pins the referenced dictionaries in
507 // memory past a possible deletion. For this reason, OnDictionaryUsed()
508 // notifications may occur after SdchOwner thinks that dictionaries
509 // have been deleted.
510 SdchManager::SdchErrorRecovery(SDCH_DICTIONARY_USED_AFTER_DELETION);
511 return;
513 base::DictionaryValue* specific_dictionary_map = nullptr;
514 success = value->GetAsDictionary(&specific_dictionary_map);
515 // TODO(rdsmith); Switch back to DCHECK() after http://crbug.com/454198 is
516 // resolved.
517 CHECK(success);
519 double last_used_seconds_since_epoch = 0.0;
520 success = specific_dictionary_map->GetDouble(kDictionaryLastUsedKey,
521 &last_used_seconds_since_epoch);
522 // TODO(rdsmith); Switch back to DCHECK() after http://crbug.com/454198 is
523 // resolved.
524 CHECK(success);
525 int use_count = 0;
526 success =
527 specific_dictionary_map->GetInteger(kDictionaryUseCountKey, &use_count);
528 // TODO(rdsmith); Switch back to DCHECK() after http://crbug.com/454198 is
529 // resolved.
530 CHECK(success);
532 if (use_counts_at_load_.count(server_hash) == 0) {
533 use_counts_at_load_[server_hash] = use_count;
536 base::TimeDelta time_since_last_used(now -
537 base::Time::FromDoubleT(last_used_seconds_since_epoch));
539 // TODO(rdsmith): Distinguish between "Never used" and "Actually not
540 // touched for 48 hours".
541 UMA_HISTOGRAM_CUSTOM_TIMES(
542 "Sdch3.UsageInterval",
543 use_count ? time_since_last_used : base::TimeDelta::FromHours(48),
544 base::TimeDelta(), base::TimeDelta::FromHours(48), 50);
546 specific_dictionary_map->SetDouble(kDictionaryLastUsedKey, now.ToDoubleT());
547 specific_dictionary_map->SetInteger(kDictionaryUseCountKey, use_count + 1);
550 void SdchOwner::OnGetDictionary(net::SdchManager* manager,
551 const GURL& request_url,
552 const GURL& dictionary_url) {
553 #if defined(OS_CHROMEOS)
554 // For debugging http://crbug.com/454198; remove when resolved.
555 char url_buf[128];
556 if (0u != destroyed_ || !clock_.get()) {
557 base::strlcpy(url_buf, request_url.spec().c_str(), arraysize(url_buf));
559 base::debug::Alias(url_buf);
561 CHECK_EQ(0u, destroyed_);
562 CHECK(clock_.get());
563 #endif
565 base::Time stale_boundary(clock_->Now() - base::TimeDelta::FromDays(1));
566 size_t avail_bytes = 0;
567 for (DictionaryPreferenceIterator it(pref_store_); !it.IsAtEnd();
568 it.Advance()) {
569 if (it.last_used() < stale_boundary)
570 avail_bytes += it.size();
573 // Don't initiate the fetch if we wouldn't be able to store any
574 // reasonable dictionary.
575 // TODO(rdsmith): Maybe do a HEAD request to figure out how much
576 // storage we'd actually need?
577 if (max_total_dictionary_size_ < (total_dictionary_bytes_ - avail_bytes +
578 min_space_for_dictionary_fetch_)) {
579 RecordDictionaryFate(DICTIONARY_FATE_GET_IGNORED);
580 // TODO(rdsmith): Log a net-internals error. This requires
581 // SdchManager to forward the URLRequest that detected the
582 // Get-Dictionary header to its observers, which is tricky
583 // because SdchManager is layered underneath URLRequest.
584 return;
587 fetcher_->Schedule(dictionary_url,
588 base::Bind(&SdchOwner::OnDictionaryFetched,
589 // SdchOwner will outlive its member variables.
590 base::Unretained(this), base::Time(), 0));
593 void SdchOwner::OnClearDictionaries(net::SdchManager* manager) {
594 total_dictionary_bytes_ = 0;
595 fetcher_->Cancel();
597 InitializePrefStore(pref_store_);
600 void SdchOwner::OnPrefValueChanged(const std::string& key) {
603 void SdchOwner::OnInitializationCompleted(bool succeeded) {
604 PersistentPrefStore::PrefReadError error =
605 external_pref_store_->GetReadError();
606 // Errors on load are self-correcting; if dictionaries were not
607 // persisted from the last instance of the browser, they will be
608 // faulted in by user action over time. However, if a load error
609 // means that the dictionary information won't be able to be persisted,
610 // the in memory pref store is left in place.
611 if (!succeeded) {
612 // Failure means a write failed, since read failures are recoverable.
613 DCHECK_NE(
614 error,
615 PersistentPrefStore::PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE);
616 DCHECK_NE(error,
617 PersistentPrefStore::PREF_READ_ERROR_MAX_ENUM);
619 LOG(ERROR) << "Pref store write failed: " << error;
620 external_pref_store_->RemoveObserver(this);
621 external_pref_store_ = nullptr;
622 RecordPersistenceFailure(PERSISTENCE_FAILURE_REASON_WRITE_FAILED);
623 return;
625 switch (external_pref_store_->GetReadError()) {
626 case PersistentPrefStore::PREF_READ_ERROR_NONE:
627 break;
629 case PersistentPrefStore::PREF_READ_ERROR_NO_FILE:
630 // First time reading; the file will be created.
631 RecordPersistenceFailure(PERSISTENCE_FAILURE_REASON_NO_FILE);
632 break;
634 case PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE:
635 case PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE:
636 case PersistentPrefStore::PREF_READ_ERROR_FILE_OTHER:
637 case PersistentPrefStore::PREF_READ_ERROR_FILE_LOCKED:
638 case PersistentPrefStore::PREF_READ_ERROR_JSON_REPEAT:
639 case PersistentPrefStore::PREF_READ_ERROR_LEVELDB_IO:
640 case PersistentPrefStore::PREF_READ_ERROR_LEVELDB_CORRUPTION_READ_ONLY:
641 case PersistentPrefStore::PREF_READ_ERROR_LEVELDB_CORRUPTION:
642 RecordPersistenceFailure(PERSISTENCE_FAILURE_REASON_READ_FAILED);
643 break;
645 case PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED:
646 case PersistentPrefStore::PREF_READ_ERROR_FILE_NOT_SPECIFIED:
647 case PersistentPrefStore::PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE:
648 case PersistentPrefStore::PREF_READ_ERROR_MAX_ENUM:
649 // Shouldn't ever happen. ACCESS_DENIED and FILE_NOT_SPECIFIED should
650 // imply !succeeded, and TASK_INCOMPLETE should never be delivered.
651 NOTREACHED();
652 break;
656 // Load in what was stored before chrome exited previously.
657 const base::Value* sdch_persistence_value = nullptr;
658 const base::DictionaryValue* sdch_persistence_dictionary = nullptr;
660 // The GetPersistentStore() routine above assumes data formatted
661 // according to the schema described at the top of this file. Since
662 // this data comes from disk, to avoid disk corruption resulting in
663 // persistent chrome errors this code avoids those assupmtions.
664 if (external_pref_store_->GetValue(kPreferenceName,
665 &sdch_persistence_value) &&
666 sdch_persistence_value->GetAsDictionary(&sdch_persistence_dictionary)) {
667 SchedulePersistedDictionaryLoads(*sdch_persistence_dictionary);
670 // Reset the persistent store and update it with the accumulated
671 // information from the local store.
672 InitializePrefStore(external_pref_store_);
674 ScopedPrefNotifier scoped_pref_notifier(external_pref_store_);
675 GetPersistentStoreDictionaryMap(external_pref_store_)
676 ->Swap(GetPersistentStoreDictionaryMap(in_memory_pref_store_.get()));
678 // This object can stop waiting on (i.e. observing) the external preference
679 // store and switch over to using it as the primary preference store.
680 pref_store_ = external_pref_store_;
681 external_pref_store_->RemoveObserver(this);
682 external_pref_store_ = nullptr;
683 in_memory_pref_store_ = nullptr;
686 void SdchOwner::SetClockForTesting(scoped_ptr<base::Clock> clock) {
687 clock_ = clock.Pass();
689 #if defined(OS_CHROMEOS)
690 // For debugging http://crbug.com/454198; remove when resolved.
691 CHECK_EQ(0u, destroyed_);
692 CHECK(clock_.get());
693 #endif
696 int SdchOwner::GetDictionaryCountForTesting() const {
697 int count = 0;
698 for (DictionaryPreferenceIterator it(pref_store_); !it.IsAtEnd();
699 it.Advance()) {
700 count++;
702 return count;
705 bool SdchOwner::HasDictionaryFromURLForTesting(const GURL& url) const {
706 for (DictionaryPreferenceIterator it(pref_store_); !it.IsAtEnd();
707 it.Advance()) {
708 if (it.url() == url)
709 return true;
711 return false;
714 void SdchOwner::SetFetcherForTesting(
715 scoped_ptr<SdchDictionaryFetcher> fetcher) {
716 fetcher_.reset(fetcher.release());
719 void SdchOwner::OnMemoryPressure(
720 base::MemoryPressureListener::MemoryPressureLevel level) {
721 DCHECK_NE(base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, level);
723 for (DictionaryPreferenceIterator it(pref_store_); !it.IsAtEnd();
724 it.Advance()) {
725 int new_uses = it.use_count() - use_counts_at_load_[it.server_hash()];
726 RecordDictionaryEvictionOrUnload(new_uses,
727 DICTIONARY_FATE_EVICT_FOR_MEMORY);
730 // TODO(rdsmith): Make a distinction between moderate and critical
731 // memory pressure.
732 manager_->ClearData();
735 bool SdchOwner::SchedulePersistedDictionaryLoads(
736 const base::DictionaryValue& persisted_info) {
737 // Any schema error will result in dropping the persisted info.
738 int version = 0;
739 if (!persisted_info.GetInteger(kVersionKey, &version))
740 return false;
742 // Any version mismatch will result in dropping the persisted info;
743 // it will be faulted in at small performance cost as URLs using
744 // dictionaries for encoding are visited.
745 if (version != kVersion)
746 return false;
748 const base::DictionaryValue* dictionary_set = nullptr;
749 if (!persisted_info.GetDictionary(kDictionariesKey, &dictionary_set))
750 return false;
752 // Any formatting error will result in skipping that particular
753 // dictionary.
754 for (base::DictionaryValue::Iterator dict_it(*dictionary_set);
755 !dict_it.IsAtEnd(); dict_it.Advance()) {
756 const base::DictionaryValue* dict_info = nullptr;
757 if (!dict_it.value().GetAsDictionary(&dict_info))
758 continue;
760 std::string url_string;
761 if (!dict_info->GetString(kDictionaryUrlKey, &url_string))
762 continue;
763 GURL dict_url(url_string);
765 double last_used;
766 if (!dict_info->GetDouble(kDictionaryLastUsedKey, &last_used))
767 continue;
769 int use_count;
770 if (!dict_info->GetInteger(kDictionaryUseCountKey, &use_count))
771 continue;
773 fetcher_->ScheduleReload(
774 dict_url, base::Bind(&SdchOwner::OnDictionaryFetched,
775 // SdchOwner will outlive its member variables.
776 base::Unretained(this),
777 base::Time::FromDoubleT(last_used),
778 use_count));
781 return true;
784 bool SdchOwner::IsPersistingDictionaries() const {
785 return in_memory_pref_store_.get() != nullptr;
788 } // namespace net