File: preinstalled_apps.cc

package info (click to toggle)
chromium 139.0.7258.127-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,122,156 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (226 lines) | stat: -rw-r--r-- 8,187 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/extensions/preinstalled_apps.h"

#include <stddef.h>

#include <array>
#include <memory>
#include <set>
#include <string>

#include "base/lazy_instance.h"
#include "base/strings/string_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/web_applications/preinstalled_app_install_features.h"
#include "chrome/browser/web_applications/preinstalled_web_app_utils.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/pref_names.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
#include "components/version_info/version_info.h"
#include "extensions/browser/extensions_browser_client.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"

namespace {

// Returns true if the app was a pre-installed app in Chrome 22
bool IsOldPreinstalledApp(const std::string& extension_id) {
  return extension_id == extension_misc::kGmailAppId ||
         extension_id == extension_misc::kYoutubeAppId;
}

bool IsLocaleSupported() {
  // Don't bother installing pre-installed apps in locales where it is known
  // that they don't work.
  // TODO(rogerta): Do this check dynamically once the webstore can expose
  // an API. See http://crbug.com/101357
  std::string locale =
      extensions::ExtensionsBrowserClient::Get()->GetApplicationLocale();
  static constexpr const char* unsupported_locales[] = {"CN", "TR", "IR"};
  for (const char* unsupported : unsupported_locales) {
    if (base::EndsWith(locale, unsupported,
                       base::CompareCase::INSENSITIVE_ASCII)) {
      return false;
    }
  }
  return true;
}

base::LazyInstance<std::set<Profile*>>::Leaky g_perform_new_installation =
    LAZY_INSTANCE_INITIALIZER;
}  // namespace

namespace preinstalled_apps {

void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
  registry->RegisterIntegerPref(prefs::kPreinstalledAppsInstallState, kUnknown);
}

// static
bool Provider::DidPerformNewInstallationForProfile(Profile* profile) {
  return g_perform_new_installation.Get().count(profile);
}

void Provider::InitProfileState() {
  // We decide to install or not install pre-installed apps based on the
  // following criteria, from highest priority to lowest priority:
  //
  // - If the locale is not compatible with the pre-installed apps, don't
  // install them.
  // - The kPreinstalledApps preferences value in the profile.  This value is
  //   usually set in the master_preferences file.
  // - If they have already been installed, don't reinstall them.

  preinstalled_apps_enabled_ =
      IsLocaleSupported() &&
      profile_->GetPrefs()->GetString(prefs::kPreinstalledApps) == "install";
  DCHECK(!perform_new_installation_);

  InstallState state = static_cast<InstallState>(
      profile_->GetPrefs()->GetInteger(prefs::kPreinstalledAppsInstallState));

  std::optional<InstallState> new_install_state;

  switch (state) {
    case kUnknown: {
      // Pre-installed apps are only installed on profile creation or a new
      // chrome download.
      bool is_new_profile = profile_->WasCreatedByVersionOrLater(
          std::string(version_info::GetVersionNumber()));
      if (is_new_profile && preinstalled_apps_enabled_) {
        new_install_state = kAlreadyInstalledPreinstalledApps;
        perform_new_installation_ = true;
      } else {
        new_install_state = kNeverInstallPreinstalledApps;
      }
      break;
    }

    // The old pre-installed apps were provided as external extensions and were
    // installed everytime Chrome was run. Thus, changing the list of default
    // apps affected all users. Migrate old pre-installed apps to new mechanism
    // where they are installed only once as INTERNAL.
    // TODO(grv) : remove after Q1-2013.
    case kProvideLegacyPreinstalledApps:
      is_migration_ = true;
      new_install_state = kAlreadyInstalledPreinstalledApps;
      break;

    case kAlreadyInstalledPreinstalledApps:
    case kNeverInstallPreinstalledApps:
      break;

    default:
      NOTREACHED();
  }

  if (new_install_state) {
    profile_->GetPrefs()->SetInteger(prefs::kPreinstalledAppsInstallState,
                                     *new_install_state);
  }
  if (perform_new_installation_)
    g_perform_new_installation.Get().insert(profile_);
}

Provider::Provider(Profile* profile,
                   VisitorInterface* service,
                   scoped_refptr<extensions::ExternalLoader> loader,
                   extensions::mojom::ManifestLocation crx_location,
                   extensions::mojom::ManifestLocation download_location,
                   int creation_flags)
    : extensions::ExternalProviderImpl(service,
                                       std::move(loader),
                                       profile,
                                       crx_location,
                                       download_location,
                                       creation_flags),
      profile_(profile) {
  DCHECK(profile);
  set_auto_acknowledge(true);

  InitProfileState();
}

void Provider::VisitRegisteredExtension() {
  if (!preinstalled_apps_enabled_) {
    // If pre-installed apps aren't enabled for the profile, we short-circuit
    // the flow to load them from the file (which happens as a result of
    // VisitRegisteredExtension()), and immediately set empty prefs.
    ExternalProviderImpl::SetPrefs(base::Value::Dict());
    return;
  }

  extensions::ExternalProviderImpl::VisitRegisteredExtension();
}

void Provider::SetPrefs(base::Value::Dict prefs) {
  DCHECK(preinstalled_apps_enabled_);

  // First, check if this is for a migration from around 2013. Likely not.
  if (is_migration_) {
    DCHECK(!perform_new_installation_);
    std::set<std::string> keys_to_erase;
    // Filter out the new pre-installed apps for migrating users, so that we
    // don't randomly install them out of the blue. Two-pass to keep iterators
    // nice and happy.
    for (auto entry : prefs) {
      if (!IsOldPreinstalledApp(entry.first))
        keys_to_erase.insert(entry.first);
    }
    for (const auto& key : keys_to_erase)
      prefs.Remove(key);
  }

  // Next, the more fun case. It's possible that these apps were uninstalled
  // as part of the web app migration. But, the web app migration could have
  // been rolled back. If that happened, we need to reinstall the extension
  // apps.
  if (!perform_new_installation_) {
    auto should_re_add_app = [profile = profile_](const std::string& id,
                                                  const base::Value& pref) {
      if (!pref.is_dict())
        return false;  // Invalid entry; it'll be ignored later.
      const std::string* web_app_flag =
          pref.GetDict().FindString(kWebAppMigrationFlag);
      if (!web_app_flag)
        return false;  // Isn't migrating.
      if (web_app::IsPreinstalledAppInstallFeatureEnabled(*web_app_flag)) {
        // The feature is still enabled; it's responsible for the behavior.
        return false;
      }
      if (!web_app::WasAppMigratedToWebApp(profile, id)) {
        // The web app was not previously migrated to a web app; don't do
        // anything special for it.
        return false;
      }

      // The edge case! We found an app that was migrated to a web app, but now
      // the feature is disabled. We need to re-add it.
      return true;
    };

    std::set<std::string> keys_to_erase;
    for (auto entry : prefs) {
      bool should_re_add = should_re_add_app(entry.first, entry.second);
      if (should_re_add) {
        // Since it will be re-added, mark it as no-longer-migrated.
        web_app::MarkAppAsMigratedToWebApp(profile_, entry.first, false);
      } else {
        keys_to_erase.insert(entry.first);
      }
    }

    for (const auto& key : keys_to_erase) {
      prefs.Remove(key);
    }
  }

  ExternalProviderImpl::SetPrefs(std::move(prefs));
}

}  // namespace preinstalled_apps