blob: dd944284cf1c440487d5d068092ac8ecf59254fe [file] [log] [blame]
[email protected]f8aefb132013-10-30 09:29:521// Copyright 2013 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/extension_sync_service.h"
6
Alexey Baskakova8ae8822019-04-04 05:09:137#include <memory>
dcheng1fc00f12015-12-26 22:18:038#include <utility>
9
treibb794dd52015-12-01 18:47:1410#include "base/auto_reset.h"
Alexey Baskakova8ae8822019-04-04 05:09:1311#include "base/bind_helpers.h"
Christopher Lamebb90202019-04-04 03:42:3612#include "base/one_shot_event.h"
[email protected]d93fcd5132014-04-24 08:42:4513#include "base/strings/utf_string_conversions.h"
[email protected]f8aefb132013-10-30 09:29:5214#include "chrome/browser/extensions/extension_service.h"
[email protected]f8aefb132013-10-30 09:29:5215#include "chrome/browser/extensions/extension_sync_data.h"
16#include "chrome/browser/extensions/extension_sync_service_factory.h"
17#include "chrome/browser/extensions/extension_util.h"
[email protected]a9f74a62014-01-10 06:48:3618#include "chrome/browser/extensions/launch_util.h"
[email protected]f8aefb132013-10-30 09:29:5219#include "chrome/browser/profiles/profile.h"
20#include "chrome/browser/sync/glue/sync_start_util.h"
Alexey Baskakova8ae8822019-04-04 05:09:1321#include "chrome/browser/web_applications/components/install_manager.h"
22#include "chrome/browser/web_applications/components/web_app_provider_base.h"
Scott Violet6200d332018-02-23 21:29:2323#include "chrome/common/buildflags.h"
[email protected]d93fcd5132014-04-24 08:42:4524#include "chrome/common/extensions/extension_constants.h"
[email protected]f8aefb132013-10-30 09:29:5225#include "chrome/common/extensions/sync_helper.h"
[email protected]d93fcd5132014-04-24 08:42:4526#include "chrome/common/web_application_info.h"
skym71603842016-10-10 18:17:3127#include "components/sync/model/sync_change.h"
28#include "components/sync/model/sync_error_factory.h"
[email protected]74474042013-11-21 12:03:5429#include "extensions/browser/app_sorting.h"
treib926ee2d2015-08-06 10:55:4230#include "extensions/browser/extension_system.h"
[email protected]411f8ae2014-05-22 11:12:2331#include "extensions/browser/extension_util.h"
[email protected]e43c61f2014-07-20 21:46:3432#include "extensions/browser/uninstall_reason.h"
[email protected]e4452d32013-11-15 23:07:4133#include "extensions/common/extension.h"
treib0c714f7c2015-07-08 10:04:5834#include "extensions/common/extension_set.h"
benwellsd4b64a72014-12-11 12:38:2735#include "extensions/common/image_util.h"
treib8668a302015-10-05 16:21:5336#include "extensions/common/permissions/permission_message_provider.h"
37#include "extensions/common/permissions/permissions_data.h"
[email protected]f8aefb132013-10-30 09:29:5238
brettw9e85ef42016-11-01 21:01:2439#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
treib40d3ad92015-10-20 18:15:4240#include "chrome/browser/supervised_user/supervised_user_service.h"
41#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
42#endif
43
treib926ee2d2015-08-06 10:55:4244using extensions::AppSorting;
[email protected]f8aefb132013-10-30 09:29:5245using extensions::Extension;
46using extensions::ExtensionPrefs;
[email protected]ca975942014-01-07 12:06:4747using extensions::ExtensionRegistry;
treib0c714f7c2015-07-08 10:04:5848using extensions::ExtensionSet;
rdevlin.cronind1aa8522015-02-13 00:25:5749using extensions::ExtensionSyncData;
treib926ee2d2015-08-06 10:55:4250using extensions::ExtensionSystem;
treib0c714f7c2015-07-08 10:04:5851using extensions::SyncBundle;
[email protected]f8aefb132013-10-30 09:29:5252
[email protected]d93fcd5132014-04-24 08:42:4553namespace {
54
treib0c714f7c2015-07-08 10:04:5855// Returns true if the sync type of |extension| matches |type|.
56bool IsCorrectSyncType(const Extension& extension, syncer::ModelType type) {
treibc644a1c2015-07-13 08:37:0457 return (type == syncer::EXTENSIONS && extension.is_extension()) ||
58 (type == syncer::APPS && extension.is_app());
treib0c714f7c2015-07-08 10:04:5859}
60
treib227b2582015-12-09 09:28:2661// Predicate for PendingExtensionManager.
Marc Treiba957f142019-04-04 12:24:1962// TODO(crbug.com/862665): The !is_theme check should be unnecessary after all
63// the bad data from crbug.com/558299 has been cleaned up.
treib227b2582015-12-09 09:28:2664bool ShouldAllowInstall(const Extension* extension) {
Devlinec30d362018-07-13 18:13:3965 return !extension->is_theme() &&
66 extensions::sync_helper::IsSyncable(extension);
treib227b2582015-12-09 09:28:2667}
68
treibc3494532015-07-21 14:51:4569syncer::SyncDataList ToSyncerSyncDataList(
70 const std::vector<ExtensionSyncData>& data) {
71 syncer::SyncDataList result;
72 result.reserve(data.size());
73 for (const ExtensionSyncData& item : data)
74 result.push_back(item.GetSyncData());
75 return result;
76}
77
Karan Bhatia2a117232017-08-23 00:24:5678static_assert(extensions::disable_reason::DISABLE_REASON_LAST == (1LL << 17),
treib29e1b9b12015-11-11 08:50:5679 "Please consider whether your new disable reason should be"
80 " syncable, and if so update this bitmask accordingly!");
81const int kKnownSyncableDisableReasons =
Minh X. Nguyen45479012017-08-18 21:35:3682 extensions::disable_reason::DISABLE_USER_ACTION |
83 extensions::disable_reason::DISABLE_PERMISSIONS_INCREASE |
84 extensions::disable_reason::DISABLE_SIDELOAD_WIPEOUT |
85 extensions::disable_reason::DISABLE_GREYLIST |
86 extensions::disable_reason::DISABLE_REMOTE_INSTALL;
87const int kAllKnownDisableReasons =
88 extensions::disable_reason::DISABLE_REASON_LAST - 1;
treib29e1b9b12015-11-11 08:50:5689// We also include any future bits for newer clients that added another disable
90// reason.
91const int kSyncableDisableReasons =
92 kKnownSyncableDisableReasons | ~kAllKnownDisableReasons;
93
[email protected]d93fcd5132014-04-24 08:42:4594} // namespace
95
treib95e800c2015-10-13 15:48:3196struct ExtensionSyncService::PendingUpdate {
97 PendingUpdate() : grant_permissions_and_reenable(false) {}
98 PendingUpdate(const base::Version& version,
99 bool grant_permissions_and_reenable)
100 : version(version),
101 grant_permissions_and_reenable(grant_permissions_and_reenable) {}
102
103 base::Version version;
104 bool grant_permissions_and_reenable;
105};
106
treib35f9ed02015-08-10 10:38:44107ExtensionSyncService::ExtensionSyncService(Profile* profile)
[email protected]f8aefb132013-10-30 09:29:52108 : profile_(profile),
treibb794dd52015-12-01 18:47:14109 ignore_updates_(false),
treib35f9ed02015-08-10 10:38:44110 flare_(sync_start_util::GetFlareForSyncableService(profile->GetPath())) {
treib8a6d9892015-08-26 10:23:19111 registry_observer_.Add(ExtensionRegistry::Get(profile_));
112 prefs_observer_.Add(ExtensionPrefs::Get(profile_));
[email protected]f8aefb132013-10-30 09:29:52113}
114
treib8a6d9892015-08-26 10:23:19115ExtensionSyncService::~ExtensionSyncService() {
116}
[email protected]f8aefb132013-10-30 09:29:52117
118// static
macourteau86b29d82015-01-23 13:24:45119ExtensionSyncService* ExtensionSyncService::Get(
120 content::BrowserContext* context) {
121 return ExtensionSyncServiceFactory::GetForBrowserContext(context);
[email protected]f8aefb132013-10-30 09:29:52122}
123
treib0c714f7c2015-07-08 10:04:58124void ExtensionSyncService::SyncExtensionChangeIfNeeded(
125 const Extension& extension) {
treib227b2582015-12-09 09:28:26126 if (ignore_updates_ || !ShouldSync(extension))
treib0c714f7c2015-07-08 10:04:58127 return;
128
129 syncer::ModelType type =
130 extension.is_app() ? syncer::APPS : syncer::EXTENSIONS;
131 SyncBundle* bundle = GetSyncBundle(type);
treibc3494532015-07-21 14:51:45132 if (bundle->IsSyncing()) {
133 bundle->PushSyncAddOrUpdate(extension.id(),
134 CreateSyncData(extension).GetSyncData());
135 DCHECK(!ExtensionPrefs::Get(profile_)->NeedsSync(extension.id()));
136 } else {
137 ExtensionPrefs::Get(profile_)->SetNeedsSync(extension.id(), true);
treib35f9ed02015-08-10 10:38:44138 if (extension_service()->is_ready() && !flare_.is_null())
treibc3494532015-07-21 14:51:45139 flare_.Run(type); // Tell sync to start ASAP.
140 }
treib0c714f7c2015-07-08 10:04:58141}
142
treib40d3ad92015-10-20 18:15:42143bool ExtensionSyncService::HasPendingReenable(
144 const std::string& id,
145 const base::Version& version) const {
146 auto it = pending_updates_.find(id);
147 if (it == pending_updates_.end())
148 return false;
149 const PendingUpdate& pending = it->second;
robpercivaldcd8b102016-01-25 19:39:00150 return pending.version == version &&
treib40d3ad92015-10-20 18:15:42151 pending.grant_permissions_and_reenable;
152}
153
Mikel Astiz3382ada2019-03-29 21:14:51154void ExtensionSyncService::WaitUntilReadyToSync(base::OnceClosure done) {
155 // Wait for the extension system to be ready.
156 ExtensionSystem::Get(profile_)->ready().Post(FROM_HERE, std::move(done));
157}
158
[email protected]f8aefb132013-10-30 09:29:52159syncer::SyncMergeResult ExtensionSyncService::MergeDataAndStartSyncing(
160 syncer::ModelType type,
161 const syncer::SyncDataList& initial_sync_data,
dchengc963c7142016-04-08 03:55:22162 std::unique_ptr<syncer::SyncChangeProcessor> sync_processor,
163 std::unique_ptr<syncer::SyncErrorFactory> sync_error_factory) {
[email protected]f8aefb132013-10-30 09:29:52164 CHECK(sync_processor.get());
treib0c714f7c2015-07-08 10:04:58165 LOG_IF(FATAL, type != syncer::EXTENSIONS && type != syncer::APPS)
166 << "Got " << type << " ModelType";
[email protected]f8aefb132013-10-30 09:29:52167
treib0c714f7c2015-07-08 10:04:58168 SyncBundle* bundle = GetSyncBundle(type);
dcheng1fc00f12015-12-26 22:18:03169 bundle->StartSyncing(std::move(sync_processor));
[email protected]f8aefb132013-10-30 09:29:52170
treibc3494532015-07-21 14:51:45171 // Apply the initial sync data, filtering out any items where we have more
172 // recent local changes. Also tell the SyncBundle the extension IDs.
173 for (const syncer::SyncData& sync_data : initial_sync_data) {
dchengc963c7142016-04-08 03:55:22174 std::unique_ptr<ExtensionSyncData> extension_sync_data(
treibc3494532015-07-21 14:51:45175 ExtensionSyncData::CreateFromSyncData(sync_data));
176 // If the extension has local state that needs to be synced, ignore this
177 // change (we assume the local state is more recent).
178 if (extension_sync_data &&
179 !ExtensionPrefs::Get(profile_)->NeedsSync(extension_sync_data->id())) {
180 ApplySyncData(*extension_sync_data);
181 }
182 }
[email protected]f8aefb132013-10-30 09:29:52183
treib0feb8282016-01-18 10:18:15184 // Now push the local state to sync.
185 // Note: We'd like to only send out changes for extensions which have
186 // NeedsSync set. However, we can't tell if our changes ever made it to the
187 // sync server (they might not e.g. when there's a temporary auth error), so
188 // we couldn't safely clear the flag. So just send out everything and let the
189 // sync client handle no-op changes.
190 std::vector<ExtensionSyncData> data_list = GetLocalSyncDataList(type);
treibc3494532015-07-21 14:51:45191 bundle->PushSyncDataList(ToSyncerSyncDataList(data_list));
treib0feb8282016-01-18 10:18:15192
treibc3494532015-07-21 14:51:45193 for (const ExtensionSyncData& data : data_list)
194 ExtensionPrefs::Get(profile_)->SetNeedsSync(data.id(), false);
[email protected]f8aefb132013-10-30 09:29:52195
treibc3494532015-07-21 14:51:45196 if (type == syncer::APPS)
treib926ee2d2015-08-06 10:55:42197 ExtensionSystem::Get(profile_)->app_sorting()->FixNTPOrdinalCollisions();
[email protected]f8aefb132013-10-30 09:29:52198
199 return syncer::SyncMergeResult(type);
200}
201
202void ExtensionSyncService::StopSyncing(syncer::ModelType type) {
treib0c714f7c2015-07-08 10:04:58203 GetSyncBundle(type)->Reset();
[email protected]f8aefb132013-10-30 09:29:52204}
205
206syncer::SyncDataList ExtensionSyncService::GetAllSyncData(
207 syncer::ModelType type) const {
treibc3494532015-07-21 14:51:45208 const SyncBundle* bundle = GetSyncBundle(type);
209 if (!bundle->IsSyncing())
210 return syncer::SyncDataList();
211
treib0feb8282016-01-18 10:18:15212 std::vector<ExtensionSyncData> sync_data_list = GetLocalSyncDataList(type);
treibc3494532015-07-21 14:51:45213
treibecc63c8d2015-09-07 16:34:47214 // Add pending data (where the local extension is not installed yet).
215 std::vector<ExtensionSyncData> pending_extensions =
216 bundle->GetPendingExtensionData();
treibc3494532015-07-21 14:51:45217 sync_data_list.insert(sync_data_list.begin(),
218 pending_extensions.begin(),
219 pending_extensions.end());
220
221 return ToSyncerSyncDataList(sync_data_list);
[email protected]f8aefb132013-10-30 09:29:52222}
223
224syncer::SyncError ExtensionSyncService::ProcessSyncChanges(
Brett Wilsone1a70422017-09-12 05:10:09225 const base::Location& from_here,
[email protected]f8aefb132013-10-30 09:29:52226 const syncer::SyncChangeList& change_list) {
treib0c714f7c2015-07-08 10:04:58227 for (const syncer::SyncChange& sync_change : change_list) {
dchengc963c7142016-04-08 03:55:22228 std::unique_ptr<ExtensionSyncData> extension_sync_data(
treibc3494532015-07-21 14:51:45229 ExtensionSyncData::CreateFromSyncChange(sync_change));
230 if (extension_sync_data)
231 ApplySyncData(*extension_sync_data);
[email protected]f8aefb132013-10-30 09:29:52232 }
233
treib926ee2d2015-08-06 10:55:42234 ExtensionSystem::Get(profile_)->app_sorting()->FixNTPOrdinalCollisions();
[email protected]f8aefb132013-10-30 09:29:52235
236 return syncer::SyncError();
237}
238
treib0c714f7c2015-07-08 10:04:58239ExtensionSyncData ExtensionSyncService::CreateSyncData(
treib926ee2d2015-08-06 10:55:42240 const Extension& extension) const {
treib29e1b9b12015-11-11 08:50:56241 const std::string& id = extension.id();
treib35f9ed02015-08-10 10:38:44242 const ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile_);
treib29e1b9b12015-11-11 08:50:56243 int disable_reasons =
244 extension_prefs->GetDisableReasons(id) & kSyncableDisableReasons;
245 // Note that we're ignoring the enabled state during ApplySyncData (we check
246 // for the existence of disable reasons instead), we're just setting it here
247 // for older Chrome versions (<M48).
Minh X. Nguyen45479012017-08-18 21:35:36248 bool enabled = (disable_reasons == extensions::disable_reason::DISABLE_NONE);
proberge901ecab2017-08-31 19:24:28249 if (extension_prefs->GetExtensionBlacklistState(extension.id()) ==
250 extensions::BLACKLISTED_MALWARE) {
251 enabled = false;
252 NOTREACHED() << "Blacklisted extensions should not be getting synced.";
253 }
254
treib29e1b9b12015-11-11 08:50:56255 bool incognito_enabled = extensions::util::IsIncognitoEnabled(id, profile_);
Minh X. Nguyen45479012017-08-18 21:35:36256 bool remote_install = extension_prefs->HasDisableReason(
257 id, extensions::disable_reason::DISABLE_REMOTE_INSTALL);
mamir192d7882016-06-22 17:10:16258 bool installed_by_custodian =
259 extensions::util::WasInstalledByCustodian(id, profile_);
treibecc63c8d2015-09-07 16:34:47260 AppSorting* app_sorting = ExtensionSystem::Get(profile_)->app_sorting();
261
Devlin Cronin56daf35132018-05-10 16:25:33262 ExtensionSyncData result =
263 extension.is_app()
264 ? ExtensionSyncData(
265 extension, enabled, disable_reasons, incognito_enabled,
266 remote_install, installed_by_custodian,
267 app_sorting->GetAppLaunchOrdinal(id),
268 app_sorting->GetPageOrdinal(id),
269 extensions::GetLaunchTypePrefValue(extension_prefs, id))
270 : ExtensionSyncData(extension, enabled, disable_reasons,
271 incognito_enabled, remote_install,
272 installed_by_custodian);
treibecc63c8d2015-09-07 16:34:47273
274 // If there's a pending update, send the new version to sync instead of the
275 // installed one.
treib29e1b9b12015-11-11 08:50:56276 auto it = pending_updates_.find(id);
treib95e800c2015-10-13 15:48:31277 if (it != pending_updates_.end()) {
278 const base::Version& version = it->second.version;
treibecc63c8d2015-09-07 16:34:47279 // If we have a pending version, it should be newer than the installed one.
Devlin Cronin03bf2d22017-12-20 08:21:05280 DCHECK_EQ(-1, extension.version().CompareTo(version));
treibecc63c8d2015-09-07 16:34:47281 result.set_version(version);
treib95e800c2015-10-13 15:48:31282 // If we'll re-enable the extension once it's updated, also send that back
283 // to sync.
284 if (it->second.grant_permissions_and_reenable)
285 result.set_enabled(true);
treib0c714f7c2015-07-08 10:04:58286 }
treibecc63c8d2015-09-07 16:34:47287 return result;
[email protected]f8aefb132013-10-30 09:29:52288}
289
treibecc63c8d2015-09-07 16:34:47290void ExtensionSyncService::ApplySyncData(
rdevlin.cronind1aa8522015-02-13 00:25:57291 const ExtensionSyncData& extension_sync_data) {
rdevlin.cronin2f1ed4c2017-06-13 16:22:13292 const std::string& id = extension_sync_data.id();
293 // Note: |extension| may be null if it hasn't been installed yet.
294 const Extension* extension =
295 ExtensionRegistry::Get(profile_)->GetInstalledExtension(id);
296 // If there is an existing extension that shouldn't be sync'd, don't
297 // apply this sync data. This can happen if the local version of an
298 // extension is default-installed, but the sync server has data from another
299 // (non-default-installed) installation. We can't apply the sync data because
300 // it would always override the local state (which would never get sync'd).
301 // See crbug.com/731824.
302 if (extension && !ShouldSync(*extension))
303 return;
304
treibb794dd52015-12-01 18:47:14305 // Ignore any pref change notifications etc. while we're applying incoming
306 // sync data, so that we don't end up notifying ourselves.
307 base::AutoReset<bool> ignore_updates(&ignore_updates_, true);
308
mamir192d7882016-06-22 17:10:16309 // Note: this may cause an existing version of the extension to be reloaded.
310 extensions::util::SetWasInstalledByCustodian(
311 extension_sync_data.id(), profile_,
312 extension_sync_data.installed_by_custodian());
313
treibc3494532015-07-21 14:51:45314 syncer::ModelType type = extension_sync_data.is_app() ? syncer::APPS
315 : syncer::EXTENSIONS;
treib227b2582015-12-09 09:28:26316 SyncBundle* bundle = GetSyncBundle(type);
asargente48ab752016-03-12 00:59:20317 DCHECK(bundle->IsSyncing());
treib227b2582015-12-09 09:28:26318 if (extension && !IsCorrectSyncType(*extension, type)) {
asargente48ab752016-03-12 00:59:20319 // The installed item isn't the same type as the sync data item, so we need
320 // to remove the sync data item; otherwise it will be a zombie that will
321 // keep coming back even if the installed item with this id is uninstalled.
322 // First tell the bundle about the extension, so that it won't just ignore
323 // the deletion, then push the deletion.
324 bundle->ApplySyncData(extension_sync_data);
325 bundle->PushSyncDeletion(id, extension_sync_data.GetSyncData());
treibecc63c8d2015-09-07 16:34:47326 return;
treib227b2582015-12-09 09:28:26327 }
treibecc63c8d2015-09-07 16:34:47328
treibecc63c8d2015-09-07 16:34:47329 // Forward to the bundle. This will just update the list of synced extensions.
treibc3494532015-07-21 14:51:45330 bundle->ApplySyncData(extension_sync_data);
331
treibecc63c8d2015-09-07 16:34:47332 // Handle uninstalls first.
333 if (extension_sync_data.uninstalled()) {
Devlin Cronin6fd1cd62017-12-05 19:13:57334 base::string16 error;
335 bool uninstalled = true;
336 if (!extension) {
337 error = base::ASCIIToUTF16("Unknown extension");
338 uninstalled = false;
339 } else {
340 uninstalled = extension_service()->UninstallExtension(
341 id, extensions::UNINSTALL_REASON_SYNC, &error);
342 }
343
344 if (!uninstalled) {
345 LOG(WARNING) << "Failed to uninstall extension with id '" << id
346 << "' from sync: " << error;
treibecc63c8d2015-09-07 16:34:47347 }
348 return;
349 }
treib0c714f7c2015-07-08 10:04:58350
treibecc63c8d2015-09-07 16:34:47351 // Extension from sync was uninstalled by the user as an external extension.
352 // Honor user choice and skip installation/enabling.
353 // TODO(treib): Should we still apply pref changes?
treib8668a302015-10-05 16:21:53354 ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile_);
355 if (extension_prefs->IsExternalExtensionUninstalled(id)) {
treibecc63c8d2015-09-07 16:34:47356 LOG(WARNING) << "Extension with id " << id
357 << " from sync was uninstalled as external extension";
358 return;
359 }
360
treib29e1b9b12015-11-11 08:50:56361 enum {
362 NOT_INSTALLED,
363 INSTALLED_OUTDATED,
364 INSTALLED_MATCHING,
365 INSTALLED_NEWER,
366 } state = NOT_INSTALLED;
367 if (extension) {
Devlin Cronin03bf2d22017-12-20 08:21:05368 switch (extension->version().CompareTo(extension_sync_data.version())) {
treib29e1b9b12015-11-11 08:50:56369 case -1: state = INSTALLED_OUTDATED; break;
370 case 0: state = INSTALLED_MATCHING; break;
371 case 1: state = INSTALLED_NEWER; break;
372 default: NOTREACHED();
373 }
374 }
375
376 // Figure out the resulting set of disable reasons.
377 int disable_reasons = extension_prefs->GetDisableReasons(id);
378
379 // Chrome versions M37-M44 used |extension_sync_data.remote_install()| to tag
380 // not-yet-approved remote installs. It's redundant now that disable reasons
381 // are synced (DISABLE_REMOTE_INSTALL should be among them already), but some
382 // old sync data may still be around, and it doesn't hurt to add the reason.
Marc Treiba957f142019-04-04 12:24:19383 // TODO(crbug.com/587804): Deprecate and eventually remove |remote_install|.
treib29e1b9b12015-11-11 08:50:56384 if (extension_sync_data.remote_install())
Minh X. Nguyen45479012017-08-18 21:35:36385 disable_reasons |= extensions::disable_reason::DISABLE_REMOTE_INSTALL;
treib29e1b9b12015-11-11 08:50:56386
387 // Add/remove disable reasons based on the incoming sync data.
388 int incoming_disable_reasons = extension_sync_data.disable_reasons();
389 if (!!incoming_disable_reasons == extension_sync_data.enabled()) {
390 // The enabled flag disagrees with the presence of disable reasons. This
treibb794dd52015-12-01 18:47:14391 // must either come from an old (<M45) client which doesn't sync disable
392 // reasons, or the extension is blacklisted (which doesn't have a
393 // corresponding disable reason).
treib29e1b9b12015-11-11 08:50:56394 // Update |disable_reasons| based on the enabled flag.
395 if (extension_sync_data.enabled())
396 disable_reasons &= ~kKnownSyncableDisableReasons;
397 else // Assume the extension was likely disabled by the user.
Minh X. Nguyen45479012017-08-18 21:35:36398 disable_reasons |= extensions::disable_reason::DISABLE_USER_ACTION;
treib29e1b9b12015-11-11 08:50:56399 } else {
400 // Replace the syncable disable reasons:
401 // 1. Remove any syncable disable reasons we might have.
402 disable_reasons &= ~kSyncableDisableReasons;
403 // 2. Add the incoming reasons. Mask with |kSyncableDisableReasons|, because
404 // Chrome M45-M47 also wrote local disable reasons to sync, and we don't
405 // want those.
406 disable_reasons |= incoming_disable_reasons & kSyncableDisableReasons;
407 }
treibecc63c8d2015-09-07 16:34:47408
409 // Enable/disable the extension.
Minh X. Nguyen45479012017-08-18 21:35:36410 bool should_be_enabled =
411 (disable_reasons == extensions::disable_reason::DISABLE_NONE);
treib95e800c2015-10-13 15:48:31412 bool reenable_after_update = false;
treib29e1b9b12015-11-11 08:50:56413 if (should_be_enabled && !extension_service()->IsExtensionEnabled(id)) {
treib8668a302015-10-05 16:21:53414 if (extension) {
415 // Only grant permissions if the sync data explicitly sets the disable
Minh X. Nguyen45479012017-08-18 21:35:36416 // reasons to extensions::disable_reason::DISABLE_NONE (as opposed to the
417 // legacy
418 // (<M45) case where they're not set at all), and if the version from sync
treib8668a302015-10-05 16:21:53419 // matches our local one.
420 bool grant_permissions = extension_sync_data.supports_disable_reasons() &&
treib29e1b9b12015-11-11 08:50:56421 (state == INSTALLED_MATCHING);
422 if (grant_permissions)
423 extension_service()->GrantPermissions(extension);
424
425 // Only enable if the extension has all required permissions.
426 // (Even if the version doesn't match - if the new version needs more
427 // permissions, it'll get disabled after the update.)
428 bool has_all_permissions =
429 grant_permissions ||
430 !extensions::PermissionMessageProvider::Get()->IsPrivilegeIncrease(
431 *extension_prefs->GetGrantedPermissions(id),
432 extension->permissions_data()->active_permissions(),
433 extension->GetType());
434 if (has_all_permissions)
435 extension_service()->EnableExtension(id);
436 else if (extension_sync_data.supports_disable_reasons())
437 reenable_after_update = true;
treib40d3ad92015-10-20 18:15:42438
brettw9e85ef42016-11-01 21:01:24439#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
treib29e1b9b12015-11-11 08:50:56440 if (!has_all_permissions && (state == INSTALLED_NEWER) &&
441 extensions::util::IsExtensionSupervised(extension, profile_)) {
442 SupervisedUserServiceFactory::GetForProfile(profile_)
Devlin Cronin03bf2d22017-12-20 08:21:05443 ->AddExtensionUpdateRequest(id, extension->version());
treib8668a302015-10-05 16:21:53444 }
treib29e1b9b12015-11-11 08:50:56445#endif
treib8668a302015-10-05 16:21:53446 } else {
447 // The extension is not installed yet. Set it to enabled; we'll check for
treib29e1b9b12015-11-11 08:50:56448 // permission increase (more accurately, for a version change) when it's
449 // actually installed.
treibecc63c8d2015-09-07 16:34:47450 extension_service()->EnableExtension(id);
treib8668a302015-10-05 16:21:53451 }
treib29e1b9b12015-11-11 08:50:56452 } else if (!should_be_enabled) {
453 // Note that |disable_reasons| includes any pre-existing reasons that
454 // weren't explicitly removed above.
455 if (extension_service()->IsExtensionEnabled(id))
456 extension_service()->DisableExtension(id, disable_reasons);
457 else // Already disabled, just replace the disable reasons.
458 extension_prefs->ReplaceDisableReasons(id, disable_reasons);
treibecc63c8d2015-09-07 16:34:47459 }
460
treibecc63c8d2015-09-07 16:34:47461 // Update the incognito flag.
462 extensions::util::SetIsIncognitoEnabled(
463 id, profile_, extension_sync_data.incognito_enabled());
464 extension = nullptr; // No longer safe to use.
465
treibecc63c8d2015-09-07 16:34:47466 // Set app-specific data.
treib0c714f7c2015-07-08 10:04:58467 if (extension_sync_data.is_app()) {
468 if (extension_sync_data.app_launch_ordinal().IsValid() &&
469 extension_sync_data.page_ordinal().IsValid()) {
treib926ee2d2015-08-06 10:55:42470 AppSorting* app_sorting = ExtensionSystem::Get(profile_)->app_sorting();
471 app_sorting->SetAppLaunchOrdinal(
treib0c714f7c2015-07-08 10:04:58472 id,
473 extension_sync_data.app_launch_ordinal());
treib926ee2d2015-08-06 10:55:42474 app_sorting->SetPageOrdinal(id, extension_sync_data.page_ordinal());
treib0c714f7c2015-07-08 10:04:58475 }
476
treibc3494532015-07-21 14:51:45477 // The corresponding validation of this value during ExtensionSyncData
478 // population is in ExtensionSyncData::ToAppSpecifics.
treib0c714f7c2015-07-08 10:04:58479 if (extension_sync_data.launch_type() >= extensions::LAUNCH_TYPE_FIRST &&
480 extension_sync_data.launch_type() < extensions::NUM_LAUNCH_TYPES) {
481 extensions::SetLaunchType(
482 profile_, id, extension_sync_data.launch_type());
483 }
484
Giovanni Ortuño Urquidib7d775d2017-07-18 03:20:16485 if (!extension_sync_data.bookmark_app_url().empty()) {
486 // Handles creating and updating the bookmark app.
treib0c714f7c2015-07-08 10:04:58487 ApplyBookmarkAppSyncData(extension_sync_data);
Giovanni Ortuño Urquidib7d775d2017-07-18 03:20:16488 return;
489 }
treib0c714f7c2015-07-08 10:04:58490 }
491
treibecc63c8d2015-09-07 16:34:47492 // Finally, trigger installation/update as required.
493 bool check_for_updates = false;
treib29e1b9b12015-11-11 08:50:56494 if (state == INSTALLED_OUTDATED) {
treibecc63c8d2015-09-07 16:34:47495 // If the extension is installed but outdated, store the new version.
treib29e1b9b12015-11-11 08:50:56496 pending_updates_[id] =
497 PendingUpdate(extension_sync_data.version(), reenable_after_update);
498 check_for_updates = true;
499 } else if (state == NOT_INSTALLED) {
treibecc63c8d2015-09-07 16:34:47500 if (!extension_service()->pending_extension_manager()->AddFromSync(
Devlinec30d362018-07-13 18:13:39501 id,
502 extension_sync_data.update_url(),
503 extension_sync_data.version(),
504 ShouldAllowInstall,
505 extension_sync_data.remote_install())) {
treibecc63c8d2015-09-07 16:34:47506 LOG(WARNING) << "Could not add pending extension for " << id;
507 // This means that the extension is already pending installation, with a
508 // non-INTERNAL location. Add to pending_sync_data, even though it will
509 // never be removed (we'll never install a syncable version of the
510 // extension), so that GetAllSyncData() continues to send it.
511 }
512 // Track pending extensions so that we can return them in GetAllSyncData().
treib227b2582015-12-09 09:28:26513 bundle->AddPendingExtensionData(extension_sync_data);
treibecc63c8d2015-09-07 16:34:47514 check_for_updates = true;
[email protected]f8aefb132013-10-30 09:29:52515 }
516
treibecc63c8d2015-09-07 16:34:47517 if (check_for_updates)
518 extension_service()->CheckForUpdatesSoon();
[email protected]f8aefb132013-10-30 09:29:52519}
520
treib0c714f7c2015-07-08 10:04:58521void ExtensionSyncService::ApplyBookmarkAppSyncData(
treib926ee2d2015-08-06 10:55:42522 const ExtensionSyncData& extension_sync_data) {
treib0c714f7c2015-07-08 10:04:58523 DCHECK(extension_sync_data.is_app());
[email protected]f8aefb132013-10-30 09:29:52524
[email protected]d93fcd5132014-04-24 08:42:45525 // Process bookmark app sync if necessary.
treib0c714f7c2015-07-08 10:04:58526 GURL bookmark_app_url(extension_sync_data.bookmark_app_url());
[email protected]d93fcd5132014-04-24 08:42:45527 if (!bookmark_app_url.is_valid() ||
treib0c714f7c2015-07-08 10:04:58528 extension_sync_data.uninstalled()) {
[email protected]d93fcd5132014-04-24 08:42:45529 return;
530 }
531
Alexey Baskakova8ae8822019-04-04 05:09:13532 auto web_app_info = std::make_unique<WebApplicationInfo>();
533 web_app_info->app_url = bookmark_app_url;
534 web_app_info->title = base::UTF8ToUTF16(extension_sync_data.name());
535 web_app_info->description =
treib0c714f7c2015-07-08 10:04:58536 base::UTF8ToUTF16(extension_sync_data.bookmark_app_description());
Alexey Baskakova8ae8822019-04-04 05:09:13537 web_app_info->scope = GURL(extension_sync_data.bookmark_app_scope());
538 web_app_info->theme_color = extension_sync_data.bookmark_app_theme_color();
Alexey Baskakov862b7c12019-05-13 07:09:11539 web_app_info->open_as_window =
540 extension_sync_data.launch_type() == extensions::LAUNCH_TYPE_WINDOW;
541
treib0c714f7c2015-07-08 10:04:58542 if (!extension_sync_data.bookmark_app_icon_color().empty()) {
limasdf3aa01fa2015-11-01 19:39:14543 extensions::image_util::ParseHexColorString(
treib0c714f7c2015-07-08 10:04:58544 extension_sync_data.bookmark_app_icon_color(),
Alexey Baskakova8ae8822019-04-04 05:09:13545 &web_app_info->generated_icon_color);
benwellsd4b64a72014-12-11 12:38:27546 }
treib0c714f7c2015-07-08 10:04:58547 for (const auto& icon : extension_sync_data.linked_icons()) {
benwellsed75d1b52015-04-29 02:39:19548 WebApplicationInfo::IconInfo icon_info;
549 icon_info.url = icon.url;
550 icon_info.width = icon.size;
551 icon_info.height = icon.size;
Alexey Baskakova8ae8822019-04-04 05:09:13552 web_app_info->icons.push_back(icon_info);
benwellsed75d1b52015-04-29 02:39:19553 }
[email protected]d93fcd5132014-04-24 08:42:45554
Alexey Baskakova8ae8822019-04-04 05:09:13555 auto* provider = web_app::WebAppProviderBase::GetProviderBase(profile_);
556 DCHECK(provider);
557
558 provider->install_manager().InstallOrUpdateWebAppFromSync(
559 extension_sync_data.id(), std::move(web_app_info), base::DoNothing());
[email protected]d93fcd5132014-04-24 08:42:45560}
561
treib35f9ed02015-08-10 10:38:44562void ExtensionSyncService::SetSyncStartFlareForTesting(
[email protected]f8aefb132013-10-30 09:29:52563 const syncer::SyncableService::StartSyncFlare& flare) {
564 flare_ = flare;
565}
566
Devlinec30d362018-07-13 18:13:39567void ExtensionSyncService::DeleteThemeDoNotUse(const Extension& theme) {
568 DCHECK(theme.is_theme());
569 GetSyncBundle(syncer::EXTENSIONS)->PushSyncDeletion(
570 theme.id(), CreateSyncData(theme).GetSyncData());
571}
572
Devlin Cronin251bd412018-05-30 00:55:42573extensions::ExtensionService* ExtensionSyncService::extension_service() const {
treib35f9ed02015-08-10 10:38:44574 return ExtensionSystem::Get(profile_)->extension_service();
575}
576
treib8a6d9892015-08-26 10:23:19577void ExtensionSyncService::OnExtensionInstalled(
578 content::BrowserContext* browser_context,
treibecc63c8d2015-09-07 16:34:47579 const Extension* extension,
treib8a6d9892015-08-26 10:23:19580 bool is_update) {
581 DCHECK_EQ(profile_, browser_context);
treibecc63c8d2015-09-07 16:34:47582 // Clear pending version if the installed one has caught up.
treib95e800c2015-10-13 15:48:31583 auto it = pending_updates_.find(extension->id());
584 if (it != pending_updates_.end()) {
Devlin Cronin03bf2d22017-12-20 08:21:05585 int compare_result = extension->version().CompareTo(it->second.version);
asargente48ab752016-03-12 00:59:20586 if (compare_result == 0 && it->second.grant_permissions_and_reenable) {
587 // The call to SyncExtensionChangeIfNeeded below will take care of syncing
588 // changes to this extension, so we don't want to trigger sync activity
589 // from the call to GrantPermissionsAndEnableExtension.
590 base::AutoReset<bool> ignore_updates(&ignore_updates_, true);
treib95e800c2015-10-13 15:48:31591 extension_service()->GrantPermissionsAndEnableExtension(extension);
asargente48ab752016-03-12 00:59:20592 }
treib95e800c2015-10-13 15:48:31593 if (compare_result >= 0)
594 pending_updates_.erase(it);
treibecc63c8d2015-09-07 16:34:47595 }
treib8a6d9892015-08-26 10:23:19596 SyncExtensionChangeIfNeeded(*extension);
597}
598
599void ExtensionSyncService::OnExtensionUninstalled(
600 content::BrowserContext* browser_context,
treibecc63c8d2015-09-07 16:34:47601 const Extension* extension,
treib8a6d9892015-08-26 10:23:19602 extensions::UninstallReason reason) {
603 DCHECK_EQ(profile_, browser_context);
604 // Don't bother syncing if the extension will be re-installed momentarily.
605 if (reason == extensions::UNINSTALL_REASON_REINSTALL ||
treib227b2582015-12-09 09:28:26606 !ShouldSync(*extension)) {
treib8a6d9892015-08-26 10:23:19607 return;
treib227b2582015-12-09 09:28:26608 }
treib8a6d9892015-08-26 10:23:19609
610 // TODO(tim): If we get here and IsSyncing is false, this will cause
611 // "back from the dead" style bugs, because sync will add-back the extension
612 // that was uninstalled here when MergeDataAndStartSyncing is called.
613 // See crbug.com/256795.
614 // Possible fix: Set NeedsSync here, then in MergeDataAndStartSyncing, if
615 // NeedsSync is set but the extension isn't installed, send a sync deletion.
treibb794dd52015-12-01 18:47:14616 if (!ignore_updates_) {
617 syncer::ModelType type =
618 extension->is_app() ? syncer::APPS : syncer::EXTENSIONS;
619 SyncBundle* bundle = GetSyncBundle(type);
620 if (bundle->IsSyncing()) {
621 bundle->PushSyncDeletion(extension->id(),
622 CreateSyncData(*extension).GetSyncData());
623 } else if (extension_service()->is_ready() && !flare_.is_null()) {
624 flare_.Run(type); // Tell sync to start ASAP.
625 }
treib8a6d9892015-08-26 10:23:19626 }
treibecc63c8d2015-09-07 16:34:47627
treib95e800c2015-10-13 15:48:31628 pending_updates_.erase(extension->id());
treib8a6d9892015-08-26 10:23:19629}
630
631void ExtensionSyncService::OnExtensionStateChanged(
632 const std::string& extension_id,
633 bool state) {
634 ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
635 const Extension* extension = registry->GetInstalledExtension(extension_id);
636 // We can get pref change notifications for extensions that aren't installed
637 // (yet). In that case, we'll pick up the change later via ExtensionRegistry
638 // observation (in OnExtensionInstalled).
639 if (extension)
640 SyncExtensionChangeIfNeeded(*extension);
641}
642
643void ExtensionSyncService::OnExtensionDisableReasonsChanged(
644 const std::string& extension_id,
645 int disabled_reasons) {
646 ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
647 const Extension* extension = registry->GetInstalledExtension(extension_id);
648 // We can get pref change notifications for extensions that aren't installed
649 // (yet). In that case, we'll pick up the change later via ExtensionRegistry
650 // observation (in OnExtensionInstalled).
651 if (extension)
652 SyncExtensionChangeIfNeeded(*extension);
653}
654
treib0c714f7c2015-07-08 10:04:58655SyncBundle* ExtensionSyncService::GetSyncBundle(syncer::ModelType type) {
656 return const_cast<SyncBundle*>(
657 const_cast<const ExtensionSyncService&>(*this).GetSyncBundle(type));
658}
659
660const SyncBundle* ExtensionSyncService::GetSyncBundle(
661 syncer::ModelType type) const {
treibc644a1c2015-07-13 08:37:04662 return (type == syncer::APPS) ? &app_sync_bundle_ : &extension_sync_bundle_;
663}
664
treibc3494532015-07-21 14:51:45665std::vector<ExtensionSyncData> ExtensionSyncService::GetLocalSyncDataList(
treib0feb8282016-01-18 10:18:15666 syncer::ModelType type) const {
treibecc63c8d2015-09-07 16:34:47667 // Collect the local state.
treib0c714f7c2015-07-08 10:04:58668 ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
treibc3494532015-07-21 14:51:45669 std::vector<ExtensionSyncData> data;
Marc Treiba957f142019-04-04 12:24:19670 // Note: Maybe we should include blacklisted/blocked extensions here, i.e.
671 // just call registry->GeneratedInstalledExtensionsSet().
treibc3494532015-07-21 14:51:45672 // It would be more consistent, but the danger is that the black/blocklist
673 // hasn't been updated on all clients by the time sync has kicked in -
674 // so it's safest not to. Take care to add any other extension lists here
675 // in the future if they are added.
treib0feb8282016-01-18 10:18:15676 FillSyncDataList(registry->enabled_extensions(), type, &data);
677 FillSyncDataList(registry->disabled_extensions(), type, &data);
678 FillSyncDataList(registry->terminated_extensions(), type, &data);
treibc3494532015-07-21 14:51:45679 return data;
treib0c714f7c2015-07-08 10:04:58680}
681
682void ExtensionSyncService::FillSyncDataList(
683 const ExtensionSet& extensions,
684 syncer::ModelType type,
685 std::vector<ExtensionSyncData>* sync_data_list) const {
treib0c714f7c2015-07-08 10:04:58686 for (const scoped_refptr<const Extension>& extension : extensions) {
treib0feb8282016-01-18 10:18:15687 if (IsCorrectSyncType(*extension, type) && ShouldSync(*extension)) {
treibecc63c8d2015-09-07 16:34:47688 // We should never have pending data for an installed extension.
689 DCHECK(!GetSyncBundle(type)->HasPendingExtensionData(extension->id()));
treib0c714f7c2015-07-08 10:04:58690 sync_data_list->push_back(CreateSyncData(*extension));
691 }
692 }
693}
treib227b2582015-12-09 09:28:26694
695bool ExtensionSyncService::ShouldSync(const Extension& extension) const {
696 // Themes are handled by the ThemeSyncableService.
697 return extensions::util::ShouldSync(&extension, profile_) &&
698 !extension.is_theme();
699}