Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / prefs / pref_hash_filter.cc
1 // Copyright 2013 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.
4
5 #include "chrome/browser/prefs/pref_hash_filter.h"
6
7 #include <algorithm>
8
9 #include "base/logging.h"
10 #include "base/metrics/histogram.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/prefs/pref_store.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/time/time.h"
15 #include "base/values.h"
16 #include "chrome/browser/prefs/pref_hash_store_transaction.h"
17 #include "chrome/browser/prefs/tracked/tracked_atomic_preference.h"
18 #include "chrome/browser/prefs/tracked/tracked_split_preference.h"
19 #include "chrome/common/pref_names.h"
20 #include "components/user_prefs/pref_registry_syncable.h"
21
22 PrefHashFilter::PrefHashFilter(
23     scoped_ptr<PrefHashStore> pref_hash_store,
24     const std::vector<TrackedPreferenceMetadata>& tracked_preferences,
25     size_t reporting_ids_count)
26         : pref_hash_store_(pref_hash_store.Pass()) {
27   DCHECK(pref_hash_store_);
28   DCHECK_GE(reporting_ids_count, tracked_preferences.size());
29
30   for (size_t i = 0; i < tracked_preferences.size(); ++i) {
31     const TrackedPreferenceMetadata& metadata = tracked_preferences[i];
32
33     scoped_ptr<TrackedPreference> tracked_preference;
34     switch (metadata.strategy) {
35       case TRACKING_STRATEGY_ATOMIC:
36         tracked_preference.reset(
37             new TrackedAtomicPreference(metadata.name, metadata.reporting_id,
38                                         reporting_ids_count,
39                                         metadata.enforcement_level));
40         break;
41       case TRACKING_STRATEGY_SPLIT:
42         tracked_preference.reset(
43             new TrackedSplitPreference(metadata.name, metadata.reporting_id,
44                                        reporting_ids_count,
45                                        metadata.enforcement_level));
46         break;
47     }
48     DCHECK(tracked_preference);
49
50     bool is_new = tracked_paths_.add(metadata.name,
51                                      tracked_preference.Pass()).second;
52     DCHECK(is_new);
53   }
54 }
55
56 PrefHashFilter::~PrefHashFilter() {
57   // Ensure new values for all |changed_paths_| have been flushed to
58   // |pref_hash_store_| already.
59   DCHECK(changed_paths_.empty());
60 }
61
62 // static
63 void PrefHashFilter::RegisterProfilePrefs(
64     user_prefs::PrefRegistrySyncable* registry) {
65   // See GetResetTime for why this is a StringPref and not Int64Pref.
66   registry->RegisterStringPref(
67       prefs::kPreferenceResetTime,
68       base::Int64ToString(base::Time().ToInternalValue()),
69       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
70 }
71
72 // static
73 base::Time PrefHashFilter::GetResetTime(PrefService* user_prefs) {
74   // Provide our own implementation (identical to the PrefService::GetInt64) in
75   // order to ensure it remains consistent with the way we store this value
76   // (which we do via a PrefStore, preventing us from reusing
77   // PrefService::SetInt64).
78   int64 internal_value = base::Time().ToInternalValue();
79   if (!base::StringToInt64(
80           user_prefs->GetString(prefs::kPreferenceResetTime),
81           &internal_value)) {
82     // Somehow the value stored on disk is not a valid int64.
83     NOTREACHED();
84     return base::Time();
85   }
86   return base::Time::FromInternalValue(internal_value);
87 }
88
89 // static
90 void PrefHashFilter::ClearResetTime(PrefService* user_prefs) {
91   user_prefs->ClearPref(prefs::kPreferenceResetTime);
92 }
93
94 void PrefHashFilter::Initialize(const PrefStore& pref_store) {
95   scoped_ptr<PrefHashStoreTransaction> hash_store_transaction(
96       pref_hash_store_->BeginTransaction());
97   for (TrackedPreferencesMap::const_iterator it = tracked_paths_.begin();
98        it != tracked_paths_.end(); ++it) {
99     const std::string& initialized_path = it->first;
100     const TrackedPreference* initialized_preference = it->second;
101     const base::Value* value = NULL;
102     pref_store.GetValue(initialized_path, &value);
103     initialized_preference->OnNewValue(value, hash_store_transaction.get());
104   }
105 }
106
107 // Marks |path| has having changed if it is part of |tracked_paths_|. A new hash
108 // will be stored for it the next time FilterSerializeData() is invoked.
109 void PrefHashFilter::FilterUpdate(const std::string& path) {
110   TrackedPreferencesMap::const_iterator it = tracked_paths_.find(path);
111   if (it != tracked_paths_.end())
112     changed_paths_.insert(std::make_pair(path, it->second));
113 }
114
115 // Updates the stored hashes for |changed_paths_| before serializing data to
116 // disk. This is required as storing the hash everytime a pref's value changes
117 // is too expensive (see perf regression @ http://crbug.com/331273).
118 void PrefHashFilter::FilterSerializeData(
119     const base::DictionaryValue* pref_store_contents) {
120   if (!changed_paths_.empty()) {
121     base::TimeTicks checkpoint = base::TimeTicks::Now();
122     {
123       scoped_ptr<PrefHashStoreTransaction> hash_store_transaction(
124           pref_hash_store_->BeginTransaction());
125       for (ChangedPathsMap::const_iterator it = changed_paths_.begin();
126            it != changed_paths_.end(); ++it) {
127         const std::string& changed_path = it->first;
128         const TrackedPreference* changed_preference = it->second;
129         const base::Value* value = NULL;
130         pref_store_contents->Get(changed_path, &value);
131         changed_preference->OnNewValue(value, hash_store_transaction.get());
132       }
133       changed_paths_.clear();
134     }
135     // TODO(gab): Remove this histogram by Feb 21 2014; after sufficient timing
136     // data has been gathered from the wild to be confident this doesn't
137     // significantly affect performance on the UI thread.
138     UMA_HISTOGRAM_TIMES("Settings.FilterSerializeDataTime",
139                         base::TimeTicks::Now() - checkpoint);
140   }
141
142   // Flush the |pref_hash_store_| to disk if it has pending writes. This is done
143   // here in an effort to flush the hash store to disk as close as possible to
144   // its matching value store (currently being flushed) to reduce the likelihood
145   // of MAC corruption in race condition scenarios where a crash occurs in the
146   // 10 seconds window where it would typically be possible that only one
147   // of the two stores has been flushed to disk (this now explicitly makes this
148   // race window as small as possible).
149   // Note that, if the |pref_hash_store_| has pending writes, this call will
150   // force serialization of its store to disk. As FilterSerializeData is already
151   // intercepting the serialization of its value store this would result in an
152   // infinite loop should the hash store also be the value store -- thus this
153   // should be removed when we move to such a model (where it will no longer be
154   // necessary anyways).
155   pref_hash_store_->CommitPendingWrite();
156 }
157
158 void PrefHashFilter::FinalizeFilterOnLoad(
159     const PostFilterOnLoadCallback& post_filter_on_load_callback,
160     scoped_ptr<base::DictionaryValue> pref_store_contents,
161     bool prefs_altered) {
162   DCHECK(pref_store_contents);
163   base::TimeTicks checkpoint = base::TimeTicks::Now();
164
165   bool did_reset = false;
166   {
167     scoped_ptr<PrefHashStoreTransaction> hash_store_transaction(
168         pref_hash_store_->BeginTransaction());
169     for (TrackedPreferencesMap::const_iterator it = tracked_paths_.begin();
170          it != tracked_paths_.end(); ++it) {
171       if (it->second->EnforceAndReport(pref_store_contents.get(),
172                                        hash_store_transaction.get())) {
173         did_reset = true;
174         prefs_altered = true;
175       }
176     }
177   }
178
179   if (did_reset) {
180     pref_store_contents->Set(prefs::kPreferenceResetTime,
181                              new base::StringValue(base::Int64ToString(
182                                  base::Time::Now().ToInternalValue())));
183   }
184
185   // TODO(gab): Remove this histogram by Feb 21 2014; after sufficient timing
186   // data has been gathered from the wild to be confident this doesn't
187   // significantly affect startup.
188   UMA_HISTOGRAM_TIMES("Settings.FilterOnLoadTime",
189                       base::TimeTicks::Now() - checkpoint);
190
191   post_filter_on_load_callback.Run(pref_store_contents.Pass(), prefs_altered);
192 }