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/browser_ui_prefs_migrator.h"
29#include "chrome/browser/prefs/command_line_pref_store.h"
30#include "chrome/browser/prefs/pref_hash_filter.h"
31#include "chrome/browser/prefs/pref_model_associator.h"
32#include "chrome/browser/prefs/pref_service_syncable.h"
33#include "chrome/browser/prefs/pref_service_syncable_factory.h"
34#include "chrome/browser/prefs/profile_pref_store_manager.h"
35#include "chrome/browser/profiles/file_path_verifier_win.h"
36#include "chrome/browser/profiles/profile.h"
37#include "chrome/browser/search_engines/default_search_pref_migration.h"
38#include "chrome/browser/sync/glue/sync_start_util.h"
39#include "chrome/browser/ui/profile_error_dialog.h"
40#include "chrome/common/chrome_constants.h"
41#include "chrome/common/pref_names.h"
42#include "chrome/grit/chromium_strings.h"
43#include "chrome/grit/generated_resources.h"
44#include "components/component_updater/pref_names.h"
45#include "components/pref_registry/pref_registry_syncable.h"
46#include "components/search_engines/default_search_manager.h"
47#include "components/search_engines/search_engines_pref_names.h"
48#include "components/sync_driver/pref_names.h"
49#include "content/public/browser/browser_context.h"
50#include "content/public/browser/browser_thread.h"
51#include "grit/browser_resources.h"
52#include "sync/internal_api/public/base/model_type.h"
53#include "ui/base/resource/resource_bundle.h"
54
55#if defined(ENABLE_CONFIGURATION_POLICY)
56#include "components/policy/core/browser/browser_policy_connector.h"
57#include "components/policy/core/browser/configuration_policy_pref_store.h"
58#include "components/policy/core/common/policy_types.h"
59#endif
60
61#if defined(ENABLE_EXTENSIONS)
62#include "extensions/browser/pref_names.h"
63#endif
64
65#if defined(ENABLE_MANAGED_USERS)
66#include "chrome/browser/supervised_user/supervised_user_pref_store.h"
67#endif
68
69#if defined(OS_WIN)
70#include "base/win/win_util.h"
71#if defined(ENABLE_RLZ)
72#include "rlz/lib/machine_id.h"
73#endif  // defined(ENABLE_RLZ)
74#endif  // defined(OS_WIN)
75
76using content::BrowserContext;
77using content::BrowserThread;
78
79namespace {
80
81// Whether we are in testing mode; can be enabled via
82// DisableDelaysAndDomainCheckForTesting(). Forces startup checks to occur
83// with no delay and ignores the presence of a domain when determining the
84// active SettingsEnforcement group.
85bool g_disable_delays_and_domain_check_for_testing = false;
86
87// These preferences must be kept in sync with the TrackedPreference enum in
88// tools/metrics/histograms/histograms.xml. To add a new preference, append it
89// to the array and add a corresponding value to the histogram enum. Each
90// tracked preference must be given a unique reporting ID.
91// See CleanupDeprecatedTrackedPreferences() in pref_hash_filter.cc to remove a
92// deprecated tracked preference.
93const PrefHashFilter::TrackedPreferenceMetadata kTrackedPrefs[] = {
94  {
95    0, prefs::kShowHomeButton,
96    PrefHashFilter::ENFORCE_ON_LOAD,
97    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
98  },
99  {
100    1, prefs::kHomePageIsNewTabPage,
101    PrefHashFilter::ENFORCE_ON_LOAD,
102    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
103  },
104  {
105    2, prefs::kHomePage,
106    PrefHashFilter::ENFORCE_ON_LOAD,
107    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
108  },
109  {
110    3, prefs::kRestoreOnStartup,
111    PrefHashFilter::ENFORCE_ON_LOAD,
112    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
113  },
114  {
115    4, prefs::kURLsToRestoreOnStartup,
116    PrefHashFilter::ENFORCE_ON_LOAD,
117    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
118  },
119#if defined(ENABLE_EXTENSIONS)
120  {
121    5, extensions::pref_names::kExtensions,
122    PrefHashFilter::NO_ENFORCEMENT,
123    PrefHashFilter::TRACKING_STRATEGY_SPLIT
124  },
125#endif
126  {
127    6, prefs::kGoogleServicesLastUsername,
128    PrefHashFilter::ENFORCE_ON_LOAD,
129    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
130  },
131  {
132    7, prefs::kSearchProviderOverrides,
133    PrefHashFilter::ENFORCE_ON_LOAD,
134    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
135  },
136  {
137    8, prefs::kDefaultSearchProviderSearchURL,
138    PrefHashFilter::ENFORCE_ON_LOAD,
139    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
140  },
141  {
142    9, prefs::kDefaultSearchProviderKeyword,
143    PrefHashFilter::ENFORCE_ON_LOAD,
144    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
145  },
146  {
147    10, prefs::kDefaultSearchProviderName,
148    PrefHashFilter::ENFORCE_ON_LOAD,
149    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
150  },
151#if !defined(OS_ANDROID)
152  {
153    11, prefs::kPinnedTabs,
154    PrefHashFilter::ENFORCE_ON_LOAD,
155    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
156  },
157  {
158    13, prefs::kProfileResetPromptMementoInProfilePrefs,
159    PrefHashFilter::ENFORCE_ON_LOAD,
160    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
161  },
162#endif
163  {
164    14, DefaultSearchManager::kDefaultSearchProviderDataPrefName,
165    PrefHashFilter::NO_ENFORCEMENT,
166    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
167  },
168  {
169    // Protecting kPreferenceResetTime does two things:
170    //  1) It ensures this isn't accidently set by someone stomping the pref
171    //     file.
172    //  2) More importantly, it declares kPreferenceResetTime as a protected
173    //     pref which is required for it to be visible when queried via the
174    //     SegregatedPrefStore. This is because it's written directly in the
175    //     protected JsonPrefStore by that store's PrefHashFilter if there was
176    //     a reset in FilterOnLoad and SegregatedPrefStore will not look for it
177    //     in the protected JsonPrefStore unless it's declared as a protected
178    //     preference here.
179    15, prefs::kPreferenceResetTime,
180    PrefHashFilter::ENFORCE_ON_LOAD,
181    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
182  },
183  {
184    16, prefs::kSafeBrowsingIncidentReportSent,
185    PrefHashFilter::ENFORCE_ON_LOAD,
186    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
187  },
188  {
189    17, sync_driver::prefs::kSyncRemainingRollbackTries,
190    PrefHashFilter::ENFORCE_ON_LOAD,
191    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
192  },
193  {
194    18, prefs::kSafeBrowsingIncidentsSent,
195    PrefHashFilter::ENFORCE_ON_LOAD,
196    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
197  },
198#if defined(OS_WIN)
199  {
200    19, prefs::kSwReporterPromptVersion,
201    PrefHashFilter::ENFORCE_ON_LOAD,
202    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
203  },
204  {
205    20, prefs::kSwReporterPromptReason,
206    PrefHashFilter::ENFORCE_ON_LOAD,
207    PrefHashFilter::TRACKING_STRATEGY_ATOMIC
208  },
209#endif
210};
211
212// One more than the last tracked preferences ID above.
213const size_t kTrackedPrefsReportingIDsCount =
214    kTrackedPrefs[arraysize(kTrackedPrefs) - 1].reporting_id + 1;
215
216// Each group enforces a superset of the protection provided by the previous
217// one.
218enum SettingsEnforcementGroup {
219  GROUP_NO_ENFORCEMENT,
220  // Enforce protected settings on profile loads.
221  GROUP_ENFORCE_ALWAYS,
222  // Also enforce extension default search.
223  GROUP_ENFORCE_ALWAYS_WITH_DSE,
224  // Also enforce extension settings and default search.
225  GROUP_ENFORCE_ALWAYS_WITH_EXTENSIONS_AND_DSE,
226  // The default enforcement group contains all protection features.
227  GROUP_ENFORCE_DEFAULT
228};
229
230SettingsEnforcementGroup GetSettingsEnforcementGroup() {
231# if defined(OS_WIN)
232  if (!g_disable_delays_and_domain_check_for_testing) {
233    static bool first_call = true;
234    static const bool is_enrolled_to_domain = base::win::IsEnrolledToDomain();
235    if (first_call) {
236      UMA_HISTOGRAM_BOOLEAN("Settings.TrackedPreferencesNoEnforcementOnDomain",
237                            is_enrolled_to_domain);
238      first_call = false;
239    }
240    if (is_enrolled_to_domain)
241      return GROUP_NO_ENFORCEMENT;
242  }
243#endif
244
245  struct {
246    const char* group_name;
247    SettingsEnforcementGroup group;
248  } static const kEnforcementLevelMap[] = {
249    { chrome_prefs::internals::kSettingsEnforcementGroupNoEnforcement,
250      GROUP_NO_ENFORCEMENT },
251    { chrome_prefs::internals::kSettingsEnforcementGroupEnforceAlways,
252      GROUP_ENFORCE_ALWAYS },
253    { chrome_prefs::internals::
254          kSettingsEnforcementGroupEnforceAlwaysWithDSE,
255      GROUP_ENFORCE_ALWAYS_WITH_DSE },
256    { chrome_prefs::internals::
257          kSettingsEnforcementGroupEnforceAlwaysWithExtensionsAndDSE,
258      GROUP_ENFORCE_ALWAYS_WITH_EXTENSIONS_AND_DSE },
259  };
260
261  // Use the strongest enforcement setting in the absence of a field trial
262  // config on Windows. Remember to update the OFFICIAL_BUILD section of
263  // extension_startup_browsertest.cc and pref_hash_browsertest.cc when updating
264  // the default value below.
265  // TODO(gab): Enforce this on all platforms.
266  SettingsEnforcementGroup enforcement_group =
267#if defined(OS_WIN)
268      GROUP_ENFORCE_DEFAULT;
269#else
270      GROUP_NO_ENFORCEMENT;
271#endif
272  bool group_determined_from_trial = false;
273  base::FieldTrial* trial =
274      base::FieldTrialList::Find(
275          chrome_prefs::internals::kSettingsEnforcementTrialName);
276  if (trial) {
277    const std::string& group_name = trial->group_name();
278    // ARRAYSIZE_UNSAFE must be used since the array is declared locally; it is
279    // only unsafe because it could not trigger a compile error on some
280    // non-array pointer types; this is fine since kEnforcementLevelMap is
281    // clearly an array.
282    for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kEnforcementLevelMap); ++i) {
283      if (kEnforcementLevelMap[i].group_name == group_name) {
284        enforcement_group = kEnforcementLevelMap[i].group;
285        group_determined_from_trial = true;
286        break;
287      }
288    }
289  }
290  UMA_HISTOGRAM_BOOLEAN("Settings.EnforcementGroupDeterminedFromTrial",
291                        group_determined_from_trial);
292  return enforcement_group;
293}
294
295// Returns the effective preference tracking configuration.
296std::vector<PrefHashFilter::TrackedPreferenceMetadata>
297GetTrackingConfiguration() {
298  const SettingsEnforcementGroup enforcement_group =
299      GetSettingsEnforcementGroup();
300
301  std::vector<PrefHashFilter::TrackedPreferenceMetadata> result;
302  for (size_t i = 0; i < arraysize(kTrackedPrefs); ++i) {
303    PrefHashFilter::TrackedPreferenceMetadata data = kTrackedPrefs[i];
304
305    if (GROUP_NO_ENFORCEMENT == enforcement_group) {
306      // Remove enforcement for all tracked preferences.
307      data.enforcement_level = PrefHashFilter::NO_ENFORCEMENT;
308    }
309
310    if (enforcement_group >= GROUP_ENFORCE_ALWAYS_WITH_DSE &&
311        data.name == DefaultSearchManager::kDefaultSearchProviderDataPrefName) {
312      // Specifically enable default search settings enforcement.
313      data.enforcement_level = PrefHashFilter::ENFORCE_ON_LOAD;
314    }
315
316#if defined(ENABLE_EXTENSIONS)
317    if (enforcement_group >= GROUP_ENFORCE_ALWAYS_WITH_EXTENSIONS_AND_DSE &&
318        data.name == extensions::pref_names::kExtensions) {
319      // Specifically enable extension settings enforcement.
320      data.enforcement_level = PrefHashFilter::ENFORCE_ON_LOAD;
321    }
322#endif
323
324    result.push_back(data);
325  }
326  return result;
327}
328
329// Shows notifications which correspond to PersistentPrefStore's reading errors.
330void HandleReadError(PersistentPrefStore::PrefReadError error) {
331  // Sample the histogram also for the successful case in order to get a
332  // baseline on the success rate in addition to the error distribution.
333  UMA_HISTOGRAM_ENUMERATION("PrefService.ReadError", error,
334                            PersistentPrefStore::PREF_READ_ERROR_MAX_ENUM);
335
336  if (error != PersistentPrefStore::PREF_READ_ERROR_NONE) {
337#if !defined(OS_CHROMEOS)
338    // Failing to load prefs on startup is a bad thing(TM). See bug 38352 for
339    // an example problem that this can cause.
340    // Do some diagnosis and try to avoid losing data.
341    int message_id = 0;
342    if (error <= PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE ||
343        error == PersistentPrefStore::PREF_READ_ERROR_LEVELDB_CORRUPTION) {
344      message_id = IDS_PREFERENCES_CORRUPT_ERROR;
345    } else if (error != PersistentPrefStore::PREF_READ_ERROR_NO_FILE) {
346      message_id = IDS_PREFERENCES_UNREADABLE_ERROR;
347    }
348
349    if (message_id) {
350      BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
351                              base::Bind(&ShowProfileErrorDialog,
352                                         PROFILE_ERROR_PREFERENCES,
353                                         message_id));
354    }
355#else
356    // On ChromeOS error screen with message about broken local state
357    // will be displayed.
358
359    // A supplementary error message about broken local state - is included
360    // in logs and user feedbacks.
361    if (error != PersistentPrefStore::PREF_READ_ERROR_NONE &&
362        error != PersistentPrefStore::PREF_READ_ERROR_NO_FILE) {
363      LOG(ERROR) << "An error happened during prefs loading: " << error;
364    }
365#endif
366  }
367}
368
369scoped_ptr<ProfilePrefStoreManager> CreateProfilePrefStoreManager(
370    const base::FilePath& profile_path) {
371  std::string device_id;
372#if defined(OS_WIN) && defined(ENABLE_RLZ)
373  // This is used by
374  // chrome/browser/extensions/api/music_manager_private/device_id_win.cc
375  // but that API is private (http://crbug.com/276485) and other platforms are
376  // not available synchronously.
377  // As part of improving pref metrics on other platforms we may want to find
378  // ways to defer preference loading until the device ID can be used.
379  rlz_lib::GetMachineId(&device_id);
380#endif
381  std::string seed;
382#if defined(GOOGLE_CHROME_BUILD)
383  seed = ResourceBundle::GetSharedInstance().GetRawDataResource(
384      IDR_PREF_HASH_SEED_BIN).as_string();
385#endif
386  return make_scoped_ptr(new ProfilePrefStoreManager(
387      profile_path,
388      GetTrackingConfiguration(),
389      kTrackedPrefsReportingIDsCount,
390      seed,
391      device_id,
392      g_browser_process->local_state()));
393}
394
395void PrepareFactory(
396    PrefServiceSyncableFactory* factory,
397    policy::PolicyService* policy_service,
398    SupervisedUserSettingsService* supervised_user_settings,
399    scoped_refptr<PersistentPrefStore> user_pref_store,
400    const scoped_refptr<PrefStore>& extension_prefs,
401    bool async) {
402#if defined(ENABLE_CONFIGURATION_POLICY)
403  using policy::ConfigurationPolicyPrefStore;
404  factory->set_managed_prefs(
405      make_scoped_refptr(new ConfigurationPolicyPrefStore(
406          policy_service,
407          g_browser_process->browser_policy_connector()->GetHandlerList(),
408          policy::POLICY_LEVEL_MANDATORY)));
409  factory->set_recommended_prefs(
410      make_scoped_refptr(new ConfigurationPolicyPrefStore(
411          policy_service,
412          g_browser_process->browser_policy_connector()->GetHandlerList(),
413          policy::POLICY_LEVEL_RECOMMENDED)));
414#endif  // ENABLE_CONFIGURATION_POLICY
415
416#if defined(ENABLE_MANAGED_USERS)
417  if (supervised_user_settings) {
418    factory->set_supervised_user_prefs(
419        make_scoped_refptr(
420            new SupervisedUserPrefStore(supervised_user_settings)));
421  }
422#endif
423
424  factory->set_async(async);
425  factory->set_extension_prefs(extension_prefs);
426  factory->set_command_line_prefs(
427      make_scoped_refptr(
428          new CommandLinePrefStore(CommandLine::ForCurrentProcess())));
429  factory->set_read_error_callback(base::Bind(&HandleReadError));
430  factory->set_user_prefs(user_pref_store);
431}
432
433}  // namespace
434
435namespace chrome_prefs {
436
437namespace internals {
438
439// Group modifications should be reflected in first_run_browsertest.cc and
440// pref_hash_browsertest.cc.
441const char kSettingsEnforcementTrialName[] = "SettingsEnforcement";
442const char kSettingsEnforcementGroupNoEnforcement[] = "no_enforcement";
443const char kSettingsEnforcementGroupEnforceAlways[] = "enforce_always";
444const char kSettingsEnforcementGroupEnforceAlwaysWithDSE[] =
445    "enforce_always_with_dse";
446const char kSettingsEnforcementGroupEnforceAlwaysWithExtensionsAndDSE[] =
447    "enforce_always_with_extensions_and_dse";
448
449}  // namespace internals
450
451scoped_ptr<PrefService> CreateLocalState(
452    const base::FilePath& pref_filename,
453    base::SequencedTaskRunner* pref_io_task_runner,
454    policy::PolicyService* policy_service,
455    const scoped_refptr<PrefRegistry>& pref_registry,
456    bool async) {
457  PrefServiceSyncableFactory factory;
458  PrepareFactory(
459      &factory,
460      policy_service,
461      NULL,  // supervised_user_settings
462      new JsonPrefStore(
463          pref_filename, pref_io_task_runner, scoped_ptr<PrefFilter>()),
464      NULL,  // extension_prefs
465      async);
466  return factory.Create(pref_registry.get());
467}
468
469scoped_ptr<PrefServiceSyncable> CreateProfilePrefs(
470    const base::FilePath& profile_path,
471    base::SequencedTaskRunner* pref_io_task_runner,
472    TrackedPreferenceValidationDelegate* validation_delegate,
473    policy::PolicyService* policy_service,
474    SupervisedUserSettingsService* supervised_user_settings,
475    const scoped_refptr<PrefStore>& extension_prefs,
476    const scoped_refptr<user_prefs::PrefRegistrySyncable>& pref_registry,
477    bool async) {
478  TRACE_EVENT0("browser", "chrome_prefs::CreateProfilePrefs");
479
480  // A StartSyncFlare used to kick sync early in case of a reset event. This is
481  // done since sync may bring back the user's server value post-reset which
482  // could potentially cause a "settings flash" between the factory default and
483  // the re-instantiated server value. Starting sync ASAP minimizes the window
484  // before the server value is re-instantiated (this window can otherwise be
485  // as long as 10 seconds by default).
486  const base::Closure start_sync_flare_for_prefs =
487      base::Bind(sync_start_util::GetFlareForSyncableService(profile_path),
488                 syncer::PREFERENCES);
489
490  PrefServiceSyncableFactory factory;
491  scoped_refptr<PersistentPrefStore> user_pref_store(
492      CreateProfilePrefStoreManager(profile_path)
493          ->CreateProfilePrefStore(pref_io_task_runner,
494                                   start_sync_flare_for_prefs,
495                                   validation_delegate));
496  // BrowserUIPrefsMigrator unregisters and deletes itself after it is done.
497  user_pref_store->AddObserver(
498      new BrowserUIPrefsMigrator(user_pref_store.get()));
499  PrepareFactory(&factory,
500                 policy_service,
501                 supervised_user_settings,
502                 user_pref_store,
503                 extension_prefs,
504                 async);
505  scoped_ptr<PrefServiceSyncable> pref_service =
506      factory.CreateSyncable(pref_registry.get());
507
508  ConfigureDefaultSearchPrefMigrationToDictionaryValue(pref_service.get());
509
510  return pref_service.Pass();
511}
512
513void SchedulePrefsFilePathVerification(const base::FilePath& profile_path) {
514#if defined(OS_WIN)
515  // Only do prefs file verification on Windows.
516  const int kVerifyPrefsFileDelaySeconds = 60;
517  BrowserThread::GetBlockingPool()->PostDelayedTask(
518      FROM_HERE,
519      base::Bind(&VerifyPreferencesFile,
520                 ProfilePrefStoreManager::GetPrefFilePathFromProfilePath(
521                     profile_path)),
522      base::TimeDelta::FromSeconds(g_disable_delays_and_domain_check_for_testing
523                                       ? 0
524                                       : kVerifyPrefsFileDelaySeconds));
525#endif
526}
527
528void DisableDelaysAndDomainCheckForTesting() {
529  g_disable_delays_and_domain_check_for_testing = true;
530}
531
532bool InitializePrefsFromMasterPrefs(
533    const base::FilePath& profile_path,
534    const base::DictionaryValue& master_prefs) {
535  return CreateProfilePrefStoreManager(profile_path)
536      ->InitializePrefsFromMasterPrefs(master_prefs);
537}
538
539base::Time GetResetTime(Profile* profile) {
540  return ProfilePrefStoreManager::GetResetTime(profile->GetPrefs());
541}
542
543void ClearResetTime(Profile* profile) {
544  ProfilePrefStoreManager::ClearResetTime(profile->GetPrefs());
545}
546
547void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
548  ProfilePrefStoreManager::RegisterProfilePrefs(registry);
549}
550
551void RegisterPrefs(PrefRegistrySimple* registry) {
552  ProfilePrefStoreManager::RegisterPrefs(registry);
553}
554
555}  // namespace chrome_prefs
556