chrome_pref_service_factory.cc revision effb81e5f8246d0db0270817048dc992db66e9fb
1// Copyright (c) 2012 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/chrome_pref_service_factory.h"
6
7#include <string>
8#include <vector>
9
10#include "base/bind.h"
11#include "base/compiler_specific.h"
12#include "base/debug/trace_event.h"
13#include "base/files/file_path.h"
14#include "base/metrics/field_trial.h"
15#include "base/metrics/histogram.h"
16#include "base/prefs/default_pref_store.h"
17#include "base/prefs/json_pref_store.h"
18#include "base/prefs/pref_filter.h"
19#include "base/prefs/pref_notifier_impl.h"
20#include "base/prefs/pref_registry.h"
21#include "base/prefs/pref_registry_simple.h"
22#include "base/prefs/pref_service.h"
23#include "base/prefs/pref_store.h"
24#include "base/prefs/pref_value_store.h"
25#include "base/threading/sequenced_worker_pool.h"
26#include "base/time/time.h"
27#include "chrome/browser/browser_process.h"
28#include "chrome/browser/prefs/command_line_pref_store.h"
29#include "chrome/browser/prefs/pref_hash_filter.h"
30#include "chrome/browser/prefs/pref_model_associator.h"
31#include "chrome/browser/prefs/pref_service_syncable.h"
32#include "chrome/browser/prefs/pref_service_syncable_factory.h"
33#include "chrome/browser/prefs/profile_pref_store_manager.h"
34#include "chrome/browser/profiles/file_path_verifier_win.h"
35#include "chrome/browser/profiles/profile_info_cache.h"
36#include "chrome/browser/profiles/profile_manager.h"
37#include "chrome/browser/ui/profile_error_dialog.h"
38#include "chrome/common/chrome_constants.h"
39#include "chrome/common/pref_names.h"
40#include "components/user_prefs/pref_registry_syncable.h"
41#include "content/public/browser/browser_context.h"
42#include "content/public/browser/browser_thread.h"
43#include "extensions/browser/pref_names.h"
44#include "grit/browser_resources.h"
45#include "grit/chromium_strings.h"
46#include "grit/generated_resources.h"
47#include "ui/base/resource/resource_bundle.h"
48
49#if defined(ENABLE_CONFIGURATION_POLICY)
50#include "components/policy/core/browser/browser_policy_connector.h"
51#include "components/policy/core/browser/configuration_policy_pref_store.h"
52#include "components/policy/core/common/policy_types.h"
53#endif
54
55#if defined(ENABLE_MANAGED_USERS)
56#include "chrome/browser/managed_mode/supervised_user_pref_store.h"
57#endif
58
59#if defined(OS_WIN)
60#include "base/win/win_util.h"
61#if defined(ENABLE_RLZ)
62#include "rlz/lib/machine_id.h"
63#endif  // defined(ENABLE_RLZ)
64#endif  // defined(OS_WIN)
65
66using content::BrowserContext;
67using content::BrowserThread;
68
69namespace {
70
71// Whether we are in testing mode; can be enabled via
72// DisableDelaysAndDomainCheckForTesting(). Forces startup checks to occur
73// with no delay and ignores the presence of a domain when determining the
74// active SettingsEnforcement group.
75bool g_disable_delays_and_domain_check_for_testing = false;
76
77// These preferences must be kept in sync with the TrackedPreference enum in
78// tools/metrics/histograms/histograms.xml. To add a new preference, append it
79// to the array and add a corresponding value to the histogram enum. Each
80// tracked preference must be given a unique reporting ID.
81const PrefHashFilter::TrackedPreferenceMetadata kTrackedPrefs[] = {
82  {
83    0, prefs::kShowHomeButton,
84    PrefHashFilter::ENFORCE_ON_LOAD,
85    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
86  },
87  {
88    1, prefs::kHomePageIsNewTabPage,
89    PrefHashFilter::ENFORCE_ON_LOAD,
90    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
91  },
92  {
93    2, prefs::kHomePage,
94    PrefHashFilter::ENFORCE_ON_LOAD,
95    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
96  },
97  {
98    3, prefs::kRestoreOnStartup,
99    PrefHashFilter::ENFORCE_ON_LOAD,
100    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
101  },
102  {
103    4, prefs::kURLsToRestoreOnStartup,
104    PrefHashFilter::ENFORCE_ON_LOAD,
105    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
106  },
107  {
108    5, extensions::pref_names::kExtensions,
109    PrefHashFilter::NO_ENFORCEMENT,
110    PrefHashFilter::TRACKING_STRATEGY_SPLIT
111  },
112  {
113    6, prefs::kGoogleServicesLastUsername,
114    PrefHashFilter::ENFORCE_ON_LOAD,
115    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
116  },
117  {
118    7, prefs::kSearchProviderOverrides,
119    PrefHashFilter::ENFORCE_ON_LOAD,
120    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
121  },
122  {
123    8, prefs::kDefaultSearchProviderSearchURL,
124    PrefHashFilter::ENFORCE_ON_LOAD,
125    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
126  },
127  {
128    9, prefs::kDefaultSearchProviderKeyword,
129    PrefHashFilter::ENFORCE_ON_LOAD,
130    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
131  },
132  {
133    10, prefs::kDefaultSearchProviderName,
134    PrefHashFilter::ENFORCE_ON_LOAD,
135    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
136  },
137#if !defined(OS_ANDROID)
138  {
139    11, prefs::kPinnedTabs,
140    PrefHashFilter::ENFORCE_ON_LOAD,
141    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
142  },
143#endif
144  {
145    12, extensions::pref_names::kKnownDisabled,
146    PrefHashFilter::NO_ENFORCEMENT,
147    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
148  },
149  {
150    13, prefs::kProfileResetPromptMemento,
151    PrefHashFilter::ENFORCE_ON_LOAD,
152    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
153  },
154};
155
156// The count of tracked preferences IDs across all platforms.
157const size_t kTrackedPrefsReportingIDsCount = 14;
158COMPILE_ASSERT(kTrackedPrefsReportingIDsCount >= arraysize(kTrackedPrefs),
159               need_to_increment_ids_count);
160
161enum SettingsEnforcementGroup {
162  GROUP_NO_ENFORCEMENT,
163  // Only enforce settings on profile loads; still allow seeding of unloaded
164  // profiles.
165  GROUP_ENFORCE_ON_LOAD,
166  GROUP_ENFORCE_ALWAYS
167};
168
169SettingsEnforcementGroup GetSettingsEnforcementGroup() {
170# if defined(OS_WIN)
171  if (!g_disable_delays_and_domain_check_for_testing) {
172    static bool first_call = true;
173    static const bool is_enrolled_to_domain = base::win::IsEnrolledToDomain();
174    if (first_call) {
175      UMA_HISTOGRAM_BOOLEAN("Settings.TrackedPreferencesNoEnforcementOnDomain",
176                            is_enrolled_to_domain);
177      first_call = false;
178    }
179    if (is_enrolled_to_domain)
180      return GROUP_NO_ENFORCEMENT;
181  }
182#endif
183
184  struct {
185    const char* group_name;
186    SettingsEnforcementGroup group;
187  } static const kEnforcementLevelMap[] = {
188    { chrome_prefs::internals::kSettingsEnforcementGroupNoEnforcement,
189      GROUP_NO_ENFORCEMENT },
190    { chrome_prefs::internals::kSettingsEnforcementGroupEnforceOnload,
191      GROUP_ENFORCE_ON_LOAD },
192    { chrome_prefs::internals::kSettingsEnforcementGroupEnforceAlways,
193      GROUP_ENFORCE_ALWAYS },
194  };
195
196  // TODO(gab): Switch the default to GROUP_ENFORCE_ALWAYS.
197  SettingsEnforcementGroup enforcement_group = GROUP_NO_ENFORCEMENT;
198  bool group_determined_from_trial = false;
199  base::FieldTrial* trial =
200      base::FieldTrialList::Find(
201          chrome_prefs::internals::kSettingsEnforcementTrialName);
202  if (trial) {
203    const std::string& group_name = trial->group_name();
204    // ARRAYSIZE_UNSAFE must be used since the array is declared locally; it is
205    // only unsafe because it could not trigger a compile error on some
206    // non-array pointer types; this is fine since kEnforcementLevelMap is
207    // clearly an array.
208    for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kEnforcementLevelMap); ++i) {
209      if (kEnforcementLevelMap[i].group_name == group_name) {
210        enforcement_group = kEnforcementLevelMap[i].group;
211        group_determined_from_trial = true;
212        break;
213      }
214    }
215  }
216  UMA_HISTOGRAM_BOOLEAN("Settings.EnforcementGroupDeterminedFromTrial",
217                        group_determined_from_trial);
218  return enforcement_group;
219}
220
221// Returns the effective preference tracking configuration.
222std::vector<PrefHashFilter::TrackedPreferenceMetadata>
223GetTrackingConfiguration() {
224  const PrefHashFilter::EnforcementLevel maximum_level =
225      GetSettingsEnforcementGroup() == GROUP_NO_ENFORCEMENT
226          ? PrefHashFilter::NO_ENFORCEMENT
227          : PrefHashFilter::ENFORCE_ON_LOAD;
228
229  std::vector<PrefHashFilter::TrackedPreferenceMetadata> result;
230  for (size_t i = 0; i < arraysize(kTrackedPrefs); ++i) {
231    PrefHashFilter::TrackedPreferenceMetadata data = kTrackedPrefs[i];
232    if (data.enforcement_level > maximum_level)
233      data.enforcement_level = maximum_level;
234    result.push_back(data);
235  }
236  return result;
237}
238
239
240// Shows notifications which correspond to PersistentPrefStore's reading errors.
241void HandleReadError(PersistentPrefStore::PrefReadError error) {
242  // Sample the histogram also for the successful case in order to get a
243  // baseline on the success rate in addition to the error distribution.
244  UMA_HISTOGRAM_ENUMERATION("PrefService.ReadError", error,
245                            PersistentPrefStore::PREF_READ_ERROR_MAX_ENUM);
246
247  if (error != PersistentPrefStore::PREF_READ_ERROR_NONE) {
248#if !defined(OS_CHROMEOS)
249    // Failing to load prefs on startup is a bad thing(TM). See bug 38352 for
250    // an example problem that this can cause.
251    // Do some diagnosis and try to avoid losing data.
252    int message_id = 0;
253    if (error <= PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE) {
254      message_id = IDS_PREFERENCES_CORRUPT_ERROR;
255    } else if (error != PersistentPrefStore::PREF_READ_ERROR_NO_FILE) {
256      message_id = IDS_PREFERENCES_UNREADABLE_ERROR;
257    }
258
259    if (message_id) {
260      BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
261                              base::Bind(&ShowProfileErrorDialog,
262                                         PROFILE_ERROR_PREFERENCES,
263                                         message_id));
264    }
265#else
266    // On ChromeOS error screen with message about broken local state
267    // will be displayed.
268#endif
269  }
270}
271
272scoped_ptr<ProfilePrefStoreManager> CreateProfilePrefStoreManager(
273    const base::FilePath& profile_path) {
274  std::string device_id;
275#if defined(OS_WIN) && defined(ENABLE_RLZ)
276  // This is used by
277  // chrome/browser/extensions/api/music_manager_private/device_id_win.cc
278  // but that API is private (http://crbug.com/276485) and other platforms are
279  // not available synchronously.
280  // As part of improving pref metrics on other platforms we may want to find
281  // ways to defer preference loading until the device ID can be used.
282  rlz_lib::GetMachineId(&device_id);
283#endif
284  return make_scoped_ptr(new ProfilePrefStoreManager(
285      profile_path,
286      GetTrackingConfiguration(),
287      kTrackedPrefsReportingIDsCount,
288      ResourceBundle::GetSharedInstance()
289          .GetRawDataResource(IDR_PREF_HASH_SEED_BIN)
290          .as_string(),
291      device_id,
292      g_browser_process->local_state()));
293}
294
295void PrepareFactory(
296    PrefServiceSyncableFactory* factory,
297    policy::PolicyService* policy_service,
298    ManagedUserSettingsService* managed_user_settings,
299    scoped_refptr<PersistentPrefStore> user_pref_store,
300    const scoped_refptr<PrefStore>& extension_prefs,
301    bool async) {
302#if defined(ENABLE_CONFIGURATION_POLICY)
303  using policy::ConfigurationPolicyPrefStore;
304  factory->set_managed_prefs(
305      make_scoped_refptr(new ConfigurationPolicyPrefStore(
306          policy_service,
307          g_browser_process->browser_policy_connector()->GetHandlerList(),
308          policy::POLICY_LEVEL_MANDATORY)));
309  factory->set_recommended_prefs(
310      make_scoped_refptr(new ConfigurationPolicyPrefStore(
311          policy_service,
312          g_browser_process->browser_policy_connector()->GetHandlerList(),
313          policy::POLICY_LEVEL_RECOMMENDED)));
314#endif  // ENABLE_CONFIGURATION_POLICY
315
316#if defined(ENABLE_MANAGED_USERS)
317  if (managed_user_settings) {
318    factory->set_supervised_user_prefs(
319        make_scoped_refptr(new SupervisedUserPrefStore(managed_user_settings)));
320  }
321#endif
322
323  factory->set_async(async);
324  factory->set_extension_prefs(extension_prefs);
325  factory->set_command_line_prefs(
326      make_scoped_refptr(
327          new CommandLinePrefStore(CommandLine::ForCurrentProcess())));
328  factory->set_read_error_callback(base::Bind(&HandleReadError));
329  factory->set_user_prefs(user_pref_store);
330}
331
332// Initialize/update preference hash stores for all profiles but the one whose
333// path matches |ignored_profile_path|.
334void UpdateAllPrefHashStoresIfRequired(
335    const base::FilePath& ignored_profile_path) {
336  if (GetSettingsEnforcementGroup() >= GROUP_ENFORCE_ALWAYS)
337    return;
338  const ProfileInfoCache& profile_info_cache =
339      g_browser_process->profile_manager()->GetProfileInfoCache();
340  const size_t n_profiles = profile_info_cache.GetNumberOfProfiles();
341  for (size_t i = 0; i < n_profiles; ++i) {
342    const base::FilePath profile_path =
343        profile_info_cache.GetPathOfProfileAtIndex(i);
344    if (profile_path != ignored_profile_path) {
345      CreateProfilePrefStoreManager(profile_path)
346          ->UpdateProfileHashStoreIfRequired(
347              JsonPrefStore::GetTaskRunnerForFile(
348                  profile_path, BrowserThread::GetBlockingPool()));
349    }
350  }
351}
352
353}  // namespace
354
355namespace chrome_prefs {
356
357namespace internals {
358
359const char kSettingsEnforcementTrialName[] = "SettingsEnforcement";
360const char kSettingsEnforcementGroupNoEnforcement[] = "no_enforcement";
361const char kSettingsEnforcementGroupEnforceOnload[] = "enforce_on_load";
362const char kSettingsEnforcementGroupEnforceAlways[] = "enforce_always";
363
364}  // namespace internals
365
366scoped_ptr<PrefService> CreateLocalState(
367    const base::FilePath& pref_filename,
368    base::SequencedTaskRunner* pref_io_task_runner,
369    policy::PolicyService* policy_service,
370    const scoped_refptr<PrefRegistry>& pref_registry,
371    bool async) {
372  PrefServiceSyncableFactory factory;
373  PrepareFactory(
374      &factory,
375      policy_service,
376      NULL,  // managed_user_settings
377      new JsonPrefStore(
378          pref_filename, pref_io_task_runner, scoped_ptr<PrefFilter>()),
379      NULL,  // extension_prefs
380      async);
381  return factory.Create(pref_registry.get());
382}
383
384scoped_ptr<PrefServiceSyncable> CreateProfilePrefs(
385    const base::FilePath& profile_path,
386    base::SequencedTaskRunner* pref_io_task_runner,
387    policy::PolicyService* policy_service,
388    ManagedUserSettingsService* managed_user_settings,
389    const scoped_refptr<PrefStore>& extension_prefs,
390    const scoped_refptr<user_prefs::PrefRegistrySyncable>& pref_registry,
391    bool async) {
392  TRACE_EVENT0("browser", "chrome_prefs::CreateProfilePrefs");
393  PrefServiceSyncableFactory factory;
394  PrepareFactory(&factory,
395                 policy_service,
396                 managed_user_settings,
397                 scoped_refptr<PersistentPrefStore>(
398                     CreateProfilePrefStoreManager(profile_path)
399                         ->CreateProfilePrefStore(pref_io_task_runner)),
400                 extension_prefs,
401                 async);
402  return factory.CreateSyncable(pref_registry.get());
403}
404
405void SchedulePrefsFilePathVerification(const base::FilePath& profile_path) {
406#if defined(OS_WIN)
407  // Only do prefs file verification on Windows.
408  const int kVerifyPrefsFileDelaySeconds = 60;
409  BrowserThread::GetBlockingPool()->PostDelayedTask(
410      FROM_HERE,
411      base::Bind(&VerifyPreferencesFile,
412                 ProfilePrefStoreManager::GetPrefFilePathFromProfilePath(
413                     profile_path)),
414      base::TimeDelta::FromSeconds(g_disable_delays_and_domain_check_for_testing
415                                       ? 0
416                                       : kVerifyPrefsFileDelaySeconds));
417#endif
418}
419
420void DisableDelaysAndDomainCheckForTesting() {
421  g_disable_delays_and_domain_check_for_testing = true;
422}
423
424void SchedulePrefHashStoresUpdateCheck(
425    const base::FilePath& initial_profile_path) {
426  if (!ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking) {
427    ProfilePrefStoreManager::ResetAllPrefHashStores(
428        g_browser_process->local_state());
429    return;
430  }
431
432  const int kDefaultPrefHashStoresUpdateCheckDelaySeconds = 55;
433  BrowserThread::PostDelayedTask(
434        BrowserThread::UI,
435        FROM_HERE,
436        base::Bind(&UpdateAllPrefHashStoresIfRequired,
437                   initial_profile_path),
438        base::TimeDelta::FromSeconds(
439            g_disable_delays_and_domain_check_for_testing ?
440                0 : kDefaultPrefHashStoresUpdateCheckDelaySeconds));
441}
442
443void ResetPrefHashStore(const base::FilePath& profile_path) {
444  CreateProfilePrefStoreManager(profile_path)->ResetPrefHashStore();
445}
446
447bool InitializePrefsFromMasterPrefs(
448    const base::FilePath& profile_path,
449    const base::DictionaryValue& master_prefs) {
450  return CreateProfilePrefStoreManager(profile_path)
451      ->InitializePrefsFromMasterPrefs(master_prefs);
452}
453
454base::Time GetResetTime(Profile* profile) {
455  return ProfilePrefStoreManager::GetResetTime(profile->GetPrefs());
456}
457
458void ClearResetTime(Profile* profile) {
459  ProfilePrefStoreManager::ClearResetTime(profile->GetPrefs());
460}
461
462void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
463  ProfilePrefStoreManager::RegisterProfilePrefs(registry);
464}
465
466void RegisterPrefs(PrefRegistrySimple* registry) {
467  ProfilePrefStoreManager::RegisterPrefs(registry);
468}
469
470}  // namespace chrome_prefs
471