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