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.
4
5#include "chrome/browser/prefs/profile_pref_store_manager.h"
6
7#include "base/bind.h"
8#include "base/callback.h"
9#include "base/files/file_util.h"
10#include "base/json/json_file_value_serializer.h"
11#include "base/logging.h"
12#include "base/metrics/histogram.h"
13#include "base/prefs/json_pref_store.h"
14#include "base/prefs/persistent_pref_store.h"
15#include "base/prefs/pref_registry_simple.h"
16#include "chrome/browser/prefs/pref_hash_store_impl.h"
17#include "chrome/browser/prefs/tracked/pref_service_hash_store_contents.h"
18#include "chrome/browser/prefs/tracked/segregated_pref_store.h"
19#include "chrome/browser/prefs/tracked/tracked_preferences_migration.h"
20#include "chrome/common/chrome_constants.h"
21#include "chrome/common/pref_names.h"
22#include "components/pref_registry/pref_registry_syncable.h"
23
24// TODO(erikwright): Enable this on Chrome OS and Android once MACs are moved
25// out of Local State. This will resolve a race condition on Android and a
26// privacy issue on ChromeOS. http://crbug.com/349158
27const bool ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking =
28#if defined(OS_ANDROID) || defined(OS_CHROMEOS)
29    false;
30#else
31    true;
32#endif
33
34ProfilePrefStoreManager::ProfilePrefStoreManager(
35    const base::FilePath& profile_path,
36    const std::vector<PrefHashFilter::TrackedPreferenceMetadata>&
37        tracking_configuration,
38    size_t reporting_ids_count,
39    const std::string& seed,
40    const std::string& device_id,
41    PrefService* local_state)
42    : profile_path_(profile_path),
43      tracking_configuration_(tracking_configuration),
44      reporting_ids_count_(reporting_ids_count),
45      seed_(seed),
46      device_id_(device_id),
47      local_state_(local_state) {}
48
49ProfilePrefStoreManager::~ProfilePrefStoreManager() {}
50
51// static
52void ProfilePrefStoreManager::RegisterPrefs(PrefRegistrySimple* registry) {
53  PrefServiceHashStoreContents::RegisterPrefs(registry);
54}
55
56// static
57void ProfilePrefStoreManager::RegisterProfilePrefs(
58    user_prefs::PrefRegistrySyncable* registry) {
59  PrefHashFilter::RegisterProfilePrefs(registry);
60}
61
62// static
63base::FilePath ProfilePrefStoreManager::GetPrefFilePathFromProfilePath(
64    const base::FilePath& profile_path) {
65  return profile_path.Append(chrome::kPreferencesFilename);
66}
67
68// static
69void ProfilePrefStoreManager::ResetAllPrefHashStores(PrefService* local_state) {
70  PrefServiceHashStoreContents::ResetAllPrefHashStores(local_state);
71}
72
73//  static
74base::Time ProfilePrefStoreManager::GetResetTime(PrefService* pref_service) {
75  return PrefHashFilter::GetResetTime(pref_service);
76}
77
78// static
79void ProfilePrefStoreManager::ClearResetTime(PrefService* pref_service) {
80  PrefHashFilter::ClearResetTime(pref_service);
81}
82
83PersistentPrefStore* ProfilePrefStoreManager::CreateProfilePrefStore(
84    const scoped_refptr<base::SequencedTaskRunner>& io_task_runner,
85    const base::Closure& on_reset_on_load,
86    TrackedPreferenceValidationDelegate* validation_delegate) {
87  scoped_ptr<PrefFilter> pref_filter;
88  if (!kPlatformSupportsPreferenceTracking) {
89    return new JsonPrefStore(GetPrefFilePathFromProfilePath(profile_path_),
90                             io_task_runner.get(),
91                             scoped_ptr<PrefFilter>());
92  }
93
94  std::vector<PrefHashFilter::TrackedPreferenceMetadata>
95      unprotected_configuration;
96  std::vector<PrefHashFilter::TrackedPreferenceMetadata>
97      protected_configuration;
98  std::set<std::string> protected_pref_names;
99  std::set<std::string> unprotected_pref_names;
100  for (std::vector<PrefHashFilter::TrackedPreferenceMetadata>::const_iterator
101           it = tracking_configuration_.begin();
102       it != tracking_configuration_.end();
103       ++it) {
104    if (it->enforcement_level > PrefHashFilter::NO_ENFORCEMENT) {
105      protected_configuration.push_back(*it);
106      protected_pref_names.insert(it->name);
107    } else {
108      unprotected_configuration.push_back(*it);
109      unprotected_pref_names.insert(it->name);
110    }
111  }
112
113  scoped_ptr<PrefHashFilter> unprotected_pref_hash_filter(
114      new PrefHashFilter(GetPrefHashStore(false),
115                         unprotected_configuration,
116                         base::Closure(),
117                         validation_delegate,
118                         reporting_ids_count_,
119                         false));
120  scoped_ptr<PrefHashFilter> protected_pref_hash_filter(
121      new PrefHashFilter(GetPrefHashStore(true),
122                         protected_configuration,
123                         on_reset_on_load,
124                         validation_delegate,
125                         reporting_ids_count_,
126                         true));
127
128  PrefHashFilter* raw_unprotected_pref_hash_filter =
129      unprotected_pref_hash_filter.get();
130  PrefHashFilter* raw_protected_pref_hash_filter =
131      protected_pref_hash_filter.get();
132
133  scoped_refptr<JsonPrefStore> unprotected_pref_store(
134      new JsonPrefStore(GetPrefFilePathFromProfilePath(profile_path_),
135                        io_task_runner.get(),
136                        unprotected_pref_hash_filter.PassAs<PrefFilter>()));
137  // TODO(gab): Remove kDeprecatedProtectedPreferencesFilename as an alternate
138  // file in M40+.
139  scoped_refptr<JsonPrefStore> protected_pref_store(new JsonPrefStore(
140      profile_path_.Append(chrome::kSecurePreferencesFilename),
141      profile_path_.Append(chrome::kProtectedPreferencesFilenameDeprecated),
142      io_task_runner.get(),
143      protected_pref_hash_filter.PassAs<PrefFilter>()));
144
145  SetupTrackedPreferencesMigration(
146      unprotected_pref_names,
147      protected_pref_names,
148      base::Bind(&JsonPrefStore::RemoveValueSilently,
149                 unprotected_pref_store->AsWeakPtr()),
150      base::Bind(&JsonPrefStore::RemoveValueSilently,
151                 protected_pref_store->AsWeakPtr()),
152      base::Bind(&JsonPrefStore::RegisterOnNextSuccessfulWriteCallback,
153                 unprotected_pref_store->AsWeakPtr()),
154      base::Bind(&JsonPrefStore::RegisterOnNextSuccessfulWriteCallback,
155                 protected_pref_store->AsWeakPtr()),
156      GetPrefHashStore(false),
157      GetPrefHashStore(true),
158      scoped_ptr<HashStoreContents>(new PrefServiceHashStoreContents(
159          profile_path_.AsUTF8Unsafe(), local_state_)),
160      raw_unprotected_pref_hash_filter,
161      raw_protected_pref_hash_filter);
162
163  return new SegregatedPrefStore(unprotected_pref_store, protected_pref_store,
164                                 protected_pref_names);
165}
166
167bool ProfilePrefStoreManager::InitializePrefsFromMasterPrefs(
168    const base::DictionaryValue& master_prefs) {
169  // Create the profile directory if it doesn't exist yet (very possible on
170  // first run).
171  if (!base::CreateDirectory(profile_path_))
172    return false;
173
174  const base::DictionaryValue* to_serialize = &master_prefs;
175  scoped_ptr<base::DictionaryValue> copy;
176
177  if (kPlatformSupportsPreferenceTracking) {
178    copy.reset(master_prefs.DeepCopy());
179    to_serialize = copy.get();
180    PrefHashFilter(GetPrefHashStore(false),
181                   tracking_configuration_,
182                   base::Closure(),
183                   NULL,
184                   reporting_ids_count_,
185                   false).Initialize(copy.get());
186  }
187
188  // This will write out to a single combined file which will be immediately
189  // migrated to two files on load.
190  JSONFileValueSerializer serializer(
191      GetPrefFilePathFromProfilePath(profile_path_));
192
193  // Call Serialize (which does IO) on the main thread, which would _normally_
194  // be verboten. In this case however, we require this IO to synchronously
195  // complete before Chrome can start (as master preferences seed the Local
196  // State and Preferences files). This won't trip ThreadIORestrictions as they
197  // won't have kicked in yet on the main thread.
198  bool success = serializer.Serialize(*to_serialize);
199
200  UMA_HISTOGRAM_BOOLEAN("Settings.InitializedFromMasterPrefs", success);
201  return success;
202}
203
204PersistentPrefStore*
205ProfilePrefStoreManager::CreateDeprecatedCombinedProfilePrefStore(
206    const scoped_refptr<base::SequencedTaskRunner>& io_task_runner) {
207  scoped_ptr<PrefFilter> pref_filter;
208  if (kPlatformSupportsPreferenceTracking) {
209    scoped_ptr<PrefHashStoreImpl> pref_hash_store_impl(
210        new PrefHashStoreImpl(seed_, device_id_, true));
211    pref_hash_store_impl->set_legacy_hash_store_contents(
212        scoped_ptr<HashStoreContents>(new PrefServiceHashStoreContents(
213            profile_path_.AsUTF8Unsafe(), local_state_)));
214    pref_filter.reset(
215        new PrefHashFilter(pref_hash_store_impl.PassAs<PrefHashStore>(),
216                           tracking_configuration_,
217                           base::Closure(),
218                           NULL,
219                           reporting_ids_count_,
220                           false));
221  }
222  return new JsonPrefStore(GetPrefFilePathFromProfilePath(profile_path_),
223                           io_task_runner.get(),
224                           pref_filter.Pass());
225}
226
227scoped_ptr<PrefHashStore> ProfilePrefStoreManager::GetPrefHashStore(
228    bool use_super_mac) {
229  DCHECK(kPlatformSupportsPreferenceTracking);
230
231  return scoped_ptr<PrefHashStore>(new PrefHashStoreImpl(
232      seed_,
233      device_id_,
234      use_super_mac));
235}
236