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/extensions/default_apps.h"
6
7#include <set>
8#include <string>
9
10#include "base/command_line.h"
11#include "base/prefs/pref_service.h"
12#include "base/strings/string_util.h"
13#include "chrome/browser/browser_process.h"
14#include "chrome/browser/first_run/first_run.h"
15#include "chrome/browser/profiles/profile.h"
16#include "chrome/common/chrome_switches.h"
17#include "chrome/common/chrome_version_info.h"
18#include "chrome/common/extensions/extension_constants.h"
19#include "chrome/common/pref_names.h"
20#include "components/pref_registry/pref_registry_syncable.h"
21#include "extensions/common/extension.h"
22
23namespace {
24
25// Returns true if the app was a default app in Chrome 22
26bool IsOldDefaultApp(const std::string& extension_id) {
27  return extension_id == extension_misc::kGmailAppId ||
28         extension_id == extension_misc::kGoogleSearchAppId ||
29         extension_id == extension_misc::kYoutubeAppId;
30}
31
32bool IsLocaleSupported() {
33  // Don't bother installing default apps in locales where it is known that
34  // they don't work.
35  // TODO(rogerta): Do this check dynamically once the webstore can expose
36  // an API. See http://crbug.com/101357
37  const std::string& locale = g_browser_process->GetApplicationLocale();
38  static const char* unsupported_locales[] = {"CN", "TR", "IR"};
39  for (size_t i = 0; i < arraysize(unsupported_locales); ++i) {
40    if (EndsWith(locale, unsupported_locales[i], false)) {
41      return false;
42    }
43  }
44  return true;
45}
46
47}  // namespace
48
49namespace default_apps {
50
51void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
52  registry->RegisterIntegerPref(
53      prefs::kDefaultAppsInstallState,
54      kUnknown,
55      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
56}
57
58bool Provider::ShouldInstallInProfile() {
59  // We decide to install or not install default apps based on the following
60  // criteria, from highest priority to lowest priority:
61  //
62  // - The command line option.  Tests use this option to disable installation
63  //   of default apps in some cases.
64  // - If the locale is not compatible with the defaults, don't install them.
65  // - The kDefaultApps preferences value in the profile.  This value is
66  //   usually set in the master_preferences file.
67  bool install_apps =
68      profile_->GetPrefs()->GetString(prefs::kDefaultApps) == "install";
69
70  InstallState state =
71      static_cast<InstallState>(profile_->GetPrefs()->GetInteger(
72          prefs::kDefaultAppsInstallState));
73
74  is_migration_ = (state == kProvideLegacyDefaultApps);
75
76  switch (state) {
77    case kUnknown: {
78      // Only new installations and profiles get default apps. In theory the
79      // new profile checks should catch new installations, but that is not
80      // always the case (http:/crbug.com/145351).
81      chrome::VersionInfo version_info;
82      bool is_new_profile =
83          profile_->WasCreatedByVersionOrLater(version_info.Version().c_str());
84      bool is_first_run = first_run::IsChromeFirstRun();
85      if (!is_first_run && !is_new_profile)
86        install_apps = false;
87      break;
88    }
89
90    // The old default apps were provided as external extensions and were
91    // installed everytime Chrome was run. Thus, changing the list of default
92    // apps affected all users. Migrate old default apps to new mechanism where
93    // they are installed only once as INTERNAL.
94    // TODO(grv) : remove after Q1-2013.
95    case kProvideLegacyDefaultApps:
96      profile_->GetPrefs()->SetInteger(
97          prefs::kDefaultAppsInstallState,
98          kAlreadyInstalledDefaultApps);
99      break;
100
101    case kAlreadyInstalledDefaultApps:
102    case kNeverInstallDefaultApps:
103      install_apps = false;
104      break;
105    default:
106      NOTREACHED();
107  }
108
109  if (install_apps && !IsLocaleSupported())
110    install_apps = false;
111
112  // Default apps are only installed on profile creation or a new chrome
113  // download.
114  if (state == kUnknown) {
115    if (install_apps) {
116      profile_->GetPrefs()->SetInteger(prefs::kDefaultAppsInstallState,
117                                       kAlreadyInstalledDefaultApps);
118    } else {
119      profile_->GetPrefs()->SetInteger(prefs::kDefaultAppsInstallState,
120                                       kNeverInstallDefaultApps);
121    }
122  }
123
124  return install_apps;
125}
126
127Provider::Provider(Profile* profile,
128                   VisitorInterface* service,
129                   extensions::ExternalLoader* loader,
130                   extensions::Manifest::Location crx_location,
131                   extensions::Manifest::Location download_location,
132                   int creation_flags)
133    : extensions::ExternalProviderImpl(service, loader, profile, crx_location,
134                                       download_location, creation_flags),
135      profile_(profile),
136      is_migration_(false) {
137  DCHECK(profile);
138  set_auto_acknowledge(true);
139}
140
141void Provider::VisitRegisteredExtension() {
142  if (!profile_ || !ShouldInstallInProfile()) {
143    base::DictionaryValue* prefs = new base::DictionaryValue;
144    SetPrefs(prefs);
145    return;
146  }
147
148  extensions::ExternalProviderImpl::VisitRegisteredExtension();
149}
150
151void Provider::SetPrefs(base::DictionaryValue* prefs) {
152  if (is_migration_) {
153    std::set<std::string> new_default_apps;
154    for (base::DictionaryValue::Iterator i(*prefs); !i.IsAtEnd(); i.Advance()) {
155      if (!IsOldDefaultApp(i.key()))
156        new_default_apps.insert(i.key());
157    }
158    // Filter out the new default apps for migrating users.
159    for (std::set<std::string>::iterator it = new_default_apps.begin();
160         it != new_default_apps.end(); ++it) {
161      prefs->Remove(*it, NULL);
162    }
163  }
164
165  ExternalProviderImpl::SetPrefs(prefs);
166}
167
168}  // namespace default_apps
169