blob: de1e6e6f994a60e5d4f7cf6db663c06c65425c07 [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
dcheng1fc00f12015-12-26 22:18:037#include <utility>
8
treibb794dd52015-12-01 18:47:149#include "base/auto_reset.h"
[email protected]d93fcd5132014-04-24 08:42:4510#include "base/strings/utf_string_conversions.h"
[email protected]d93fcd5132014-04-24 08:42:4511#include "chrome/browser/extensions/bookmark_app_helper.h"
[email protected]f8aefb132013-10-30 09:29:5212#include "chrome/browser/extensions/extension_service.h"
[email protected]f8aefb132013-10-30 09:29:5213#include "chrome/browser/extensions/extension_sync_data.h"
14#include "chrome/browser/extensions/extension_sync_service_factory.h"
15#include "chrome/browser/extensions/extension_util.h"
[email protected]a9f74a62014-01-10 06:48:3616#include "chrome/browser/extensions/launch_util.h"
rdevlin.cronind01837b2016-08-17 01:37:1817#include "chrome/browser/extensions/scripting_permissions_modifier.h"
[email protected]f8aefb132013-10-30 09:29:5218#include "chrome/browser/profiles/profile.h"
19#include "chrome/browser/sync/glue/sync_start_util.h"
[email protected]d93fcd5132014-04-24 08:42:4520#include "chrome/common/extensions/extension_constants.h"
[email protected]f8aefb132013-10-30 09:29:5221#include "chrome/common/extensions/sync_helper.h"
brettw9e85ef42016-11-01 21:01:2422#include "chrome/common/features.h"
[email protected]d93fcd5132014-04-24 08:42:4523#include "chrome/common/web_application_info.h"
skym71603842016-10-10 18:17:3124#include "components/sync/model/sync_change.h"
25#include "components/sync/model/sync_error_factory.h"
[email protected]74474042013-11-21 12:03:5426#include "extensions/browser/app_sorting.h"
[email protected]489db0842014-01-22 18:20:0327#include "extensions/browser/extension_prefs.h"
[email protected]ca975942014-01-07 12:06:4728#include "extensions/browser/extension_registry.h"
treib926ee2d2015-08-06 10:55:4229#include "extensions/browser/extension_system.h"
[email protected]411f8ae2014-05-22 11:12:2330#include "extensions/browser/extension_util.h"
[email protected]e43c61f2014-07-20 21:46:3431#include "extensions/browser/uninstall_reason.h"
[email protected]e4452d32013-11-15 23:07:4132#include "extensions/common/extension.h"
treib0c714f7c2015-07-08 10:04:5833#include "extensions/common/extension_set.h"
benwellsd4b64a72014-12-11 12:38:2734#include "extensions/common/image_util.h"
treib8668a302015-10-05 16:21:5335#include "extensions/common/permissions/permission_message_provider.h"
36#include "extensions/common/permissions/permissions_data.h"
[email protected]f8aefb132013-10-30 09:29:5237
brettw9e85ef42016-11-01 21:01:2438#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
treib40d3ad92015-10-20 18:15:4239#include "chrome/browser/supervised_user/supervised_user_service.h"
40#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
41#endif
42
treib926ee2d2015-08-06 10:55:4243using extensions::AppSorting;
[email protected]f8aefb132013-10-30 09:29:5244using extensions::Extension;
45using extensions::ExtensionPrefs;
[email protected]ca975942014-01-07 12:06:4746using extensions::ExtensionRegistry;
treib0c714f7c2015-07-08 10:04:5847using extensions::ExtensionSet;
rdevlin.cronind1aa8522015-02-13 00:25:5748using extensions::ExtensionSyncData;
treib926ee2d2015-08-06 10:55:4249using extensions::ExtensionSystem;
treib0c714f7c2015-07-08 10:04:5850using extensions::SyncBundle;
[email protected]f8aefb132013-10-30 09:29:5251
[email protected]d93fcd5132014-04-24 08:42:4552namespace {
53
rdevlin.cronind1aa8522015-02-13 00:25:5754// Returns the pref value for "all urls enabled" for the given extension id.
55ExtensionSyncData::OptionalBoolean GetAllowedOnAllUrlsOptionalBoolean(
rdevlin.cronind01837b2016-08-17 01:37:1856 const Extension& extension,
rdevlin.cronind1aa8522015-02-13 00:25:5757 content::BrowserContext* context) {
rdevlin.cronind01837b2016-08-17 01:37:1858 extensions::ScriptingPermissionsModifier permissions_modifier(context,
59 &extension);
60 bool allowed_on_all_urls = permissions_modifier.IsAllowedOnAllUrls();
rdevlin.cronind1aa8522015-02-13 00:25:5761 // If the extension is not allowed on all urls (which is not the default),
62 // then we have to sync the preference.
63 if (!allowed_on_all_urls)
64 return ExtensionSyncData::BOOLEAN_FALSE;
65
66 // If the user has explicitly set a value, then we sync it.
rdevlin.cronind01837b2016-08-17 01:37:1867 if (permissions_modifier.HasSetAllowedOnAllUrls())
rdevlin.cronind1aa8522015-02-13 00:25:5768 return ExtensionSyncData::BOOLEAN_TRUE;
69
70 // Otherwise, unset.
71 return ExtensionSyncData::BOOLEAN_UNSET;
72}
73
treib0c714f7c2015-07-08 10:04:5874// Returns true if the sync type of |extension| matches |type|.
75bool IsCorrectSyncType(const Extension& extension, syncer::ModelType type) {
treibc644a1c2015-07-13 08:37:0476 return (type == syncer::EXTENSIONS && extension.is_extension()) ||
77 (type == syncer::APPS && extension.is_app());
treib0c714f7c2015-07-08 10:04:5878}
79
treib227b2582015-12-09 09:28:2680// Predicate for PendingExtensionManager.
81// TODO(treib,devlin): The !is_theme check shouldn't be necessary anymore after
82// all the bad data from crbug.com/558299 has been cleaned up, after M52 or so.
83bool ShouldAllowInstall(const Extension* extension) {
84 return !extension->is_theme() &&
85 extensions::sync_helper::IsSyncable(extension);
86}
87
treibc3494532015-07-21 14:51:4588syncer::SyncDataList ToSyncerSyncDataList(
89 const std::vector<ExtensionSyncData>& data) {
90 syncer::SyncDataList result;
91 result.reserve(data.size());
92 for (const ExtensionSyncData& item : data)
93 result.push_back(item.GetSyncData());
94 return result;
95}
96
Karan Bhatia2a117232017-08-23 00:24:5697static_assert(extensions::disable_reason::DISABLE_REASON_LAST == (1LL << 17),
treib29e1b9b12015-11-11 08:50:5698 "Please consider whether your new disable reason should be"
99 " syncable, and if so update this bitmask accordingly!");
100const int kKnownSyncableDisableReasons =
Minh X. Nguyen45479012017-08-18 21:35:36101 extensions::disable_reason::DISABLE_USER_ACTION |
102 extensions::disable_reason::DISABLE_PERMISSIONS_INCREASE |
103 extensions::disable_reason::DISABLE_SIDELOAD_WIPEOUT |
104 extensions::disable_reason::DISABLE_GREYLIST |
105 extensions::disable_reason::DISABLE_REMOTE_INSTALL;
106const int kAllKnownDisableReasons =
107 extensions::disable_reason::DISABLE_REASON_LAST - 1;
treib29e1b9b12015-11-11 08:50:56108// We also include any future bits for newer clients that added another disable
109// reason.
110const int kSyncableDisableReasons =
111 kKnownSyncableDisableReasons | ~kAllKnownDisableReasons;
112
[email protected]d93fcd5132014-04-24 08:42:45113} // namespace
114
treib95e800c2015-10-13 15:48:31115struct ExtensionSyncService::PendingUpdate {
116 PendingUpdate() : grant_permissions_and_reenable(false) {}
117 PendingUpdate(const base::Version& version,
118 bool grant_permissions_and_reenable)
119 : version(version),
120 grant_permissions_and_reenable(grant_permissions_and_reenable) {}
121
122 base::Version version;
123 bool grant_permissions_and_reenable;
124};
125
treib35f9ed02015-08-10 10:38:44126ExtensionSyncService::ExtensionSyncService(Profile* profile)
[email protected]f8aefb132013-10-30 09:29:52127 : profile_(profile),
treib8a6d9892015-08-26 10:23:19128 registry_observer_(this),
129 prefs_observer_(this),
treibb794dd52015-12-01 18:47:14130 ignore_updates_(false),
treib35f9ed02015-08-10 10:38:44131 flare_(sync_start_util::GetFlareForSyncableService(profile->GetPath())) {
treib8a6d9892015-08-26 10:23:19132 registry_observer_.Add(ExtensionRegistry::Get(profile_));
133 prefs_observer_.Add(ExtensionPrefs::Get(profile_));
[email protected]f8aefb132013-10-30 09:29:52134}
135
treib8a6d9892015-08-26 10:23:19136ExtensionSyncService::~ExtensionSyncService() {
137}
[email protected]f8aefb132013-10-30 09:29:52138
139// static
macourteau86b29d82015-01-23 13:24:45140ExtensionSyncService* ExtensionSyncService::Get(
141 content::BrowserContext* context) {
142 return ExtensionSyncServiceFactory::GetForBrowserContext(context);
[email protected]f8aefb132013-10-30 09:29:52143}
144
treib0c714f7c2015-07-08 10:04:58145void ExtensionSyncService::SyncExtensionChangeIfNeeded(
146 const Extension& extension) {
treib227b2582015-12-09 09:28:26147 if (ignore_updates_ || !ShouldSync(extension))
treib0c714f7c2015-07-08 10:04:58148 return;
149
150 syncer::ModelType type =
151 extension.is_app() ? syncer::APPS : syncer::EXTENSIONS;
152 SyncBundle* bundle = GetSyncBundle(type);
treibc3494532015-07-21 14:51:45153 if (bundle->IsSyncing()) {
154 bundle->PushSyncAddOrUpdate(extension.id(),
155 CreateSyncData(extension).GetSyncData());
156 DCHECK(!ExtensionPrefs::Get(profile_)->NeedsSync(extension.id()));
157 } else {
158 ExtensionPrefs::Get(profile_)->SetNeedsSync(extension.id(), true);
treib35f9ed02015-08-10 10:38:44159 if (extension_service()->is_ready() && !flare_.is_null())
treibc3494532015-07-21 14:51:45160 flare_.Run(type); // Tell sync to start ASAP.
161 }
treib0c714f7c2015-07-08 10:04:58162}
163
treib40d3ad92015-10-20 18:15:42164bool ExtensionSyncService::HasPendingReenable(
165 const std::string& id,
166 const base::Version& version) const {
167 auto it = pending_updates_.find(id);
168 if (it == pending_updates_.end())
169 return false;
170 const PendingUpdate& pending = it->second;
robpercivaldcd8b102016-01-25 19:39:00171 return pending.version == version &&
treib40d3ad92015-10-20 18:15:42172 pending.grant_permissions_and_reenable;
173}
174
[email protected]f8aefb132013-10-30 09:29:52175syncer::SyncMergeResult ExtensionSyncService::MergeDataAndStartSyncing(
176 syncer::ModelType type,
177 const syncer::SyncDataList& initial_sync_data,
dchengc963c7142016-04-08 03:55:22178 std::unique_ptr<syncer::SyncChangeProcessor> sync_processor,
179 std::unique_ptr<syncer::SyncErrorFactory> sync_error_factory) {
[email protected]f8aefb132013-10-30 09:29:52180 CHECK(sync_processor.get());
treib0c714f7c2015-07-08 10:04:58181 LOG_IF(FATAL, type != syncer::EXTENSIONS && type != syncer::APPS)
182 << "Got " << type << " ModelType";
[email protected]f8aefb132013-10-30 09:29:52183
treib0c714f7c2015-07-08 10:04:58184 SyncBundle* bundle = GetSyncBundle(type);
dcheng1fc00f12015-12-26 22:18:03185 bundle->StartSyncing(std::move(sync_processor));
[email protected]f8aefb132013-10-30 09:29:52186
treibc3494532015-07-21 14:51:45187 // Apply the initial sync data, filtering out any items where we have more
188 // recent local changes. Also tell the SyncBundle the extension IDs.
189 for (const syncer::SyncData& sync_data : initial_sync_data) {
dchengc963c7142016-04-08 03:55:22190 std::unique_ptr<ExtensionSyncData> extension_sync_data(
treibc3494532015-07-21 14:51:45191 ExtensionSyncData::CreateFromSyncData(sync_data));
192 // If the extension has local state that needs to be synced, ignore this
193 // change (we assume the local state is more recent).
194 if (extension_sync_data &&
195 !ExtensionPrefs::Get(profile_)->NeedsSync(extension_sync_data->id())) {
196 ApplySyncData(*extension_sync_data);
197 }
198 }
[email protected]f8aefb132013-10-30 09:29:52199
treib0feb8282016-01-18 10:18:15200 // Now push the local state to sync.
201 // Note: We'd like to only send out changes for extensions which have
202 // NeedsSync set. However, we can't tell if our changes ever made it to the
203 // sync server (they might not e.g. when there's a temporary auth error), so
204 // we couldn't safely clear the flag. So just send out everything and let the
205 // sync client handle no-op changes.
206 std::vector<ExtensionSyncData> data_list = GetLocalSyncDataList(type);
treibc3494532015-07-21 14:51:45207 bundle->PushSyncDataList(ToSyncerSyncDataList(data_list));
treib0feb8282016-01-18 10:18:15208
treibc3494532015-07-21 14:51:45209 for (const ExtensionSyncData& data : data_list)
210 ExtensionPrefs::Get(profile_)->SetNeedsSync(data.id(), false);
[email protected]f8aefb132013-10-30 09:29:52211
treibc3494532015-07-21 14:51:45212 if (type == syncer::APPS)
treib926ee2d2015-08-06 10:55:42213 ExtensionSystem::Get(profile_)->app_sorting()->FixNTPOrdinalCollisions();
[email protected]f8aefb132013-10-30 09:29:52214
215 return syncer::SyncMergeResult(type);
216}
217
218void ExtensionSyncService::StopSyncing(syncer::ModelType type) {
treib0c714f7c2015-07-08 10:04:58219 GetSyncBundle(type)->Reset();
[email protected]f8aefb132013-10-30 09:29:52220}
221
222syncer::SyncDataList ExtensionSyncService::GetAllSyncData(
223 syncer::ModelType type) const {
treibc3494532015-07-21 14:51:45224 const SyncBundle* bundle = GetSyncBundle(type);
225 if (!bundle->IsSyncing())
226 return syncer::SyncDataList();
227
treib0feb8282016-01-18 10:18:15228 std::vector<ExtensionSyncData> sync_data_list = GetLocalSyncDataList(type);
treibc3494532015-07-21 14:51:45229
treibecc63c8d2015-09-07 16:34:47230 // Add pending data (where the local extension is not installed yet).
231 std::vector<ExtensionSyncData> pending_extensions =
232 bundle->GetPendingExtensionData();
treibc3494532015-07-21 14:51:45233 sync_data_list.insert(sync_data_list.begin(),
234 pending_extensions.begin(),
235 pending_extensions.end());
236
237 return ToSyncerSyncDataList(sync_data_list);
[email protected]f8aefb132013-10-30 09:29:52238}
239
240syncer::SyncError ExtensionSyncService::ProcessSyncChanges(
Brett Wilsone1a70422017-09-12 05:10:09241 const base::Location& from_here,
[email protected]f8aefb132013-10-30 09:29:52242 const syncer::SyncChangeList& change_list) {
treib0c714f7c2015-07-08 10:04:58243 for (const syncer::SyncChange& sync_change : change_list) {
dchengc963c7142016-04-08 03:55:22244 std::unique_ptr<ExtensionSyncData> extension_sync_data(
treibc3494532015-07-21 14:51:45245 ExtensionSyncData::CreateFromSyncChange(sync_change));
246 if (extension_sync_data)
247 ApplySyncData(*extension_sync_data);
[email protected]f8aefb132013-10-30 09:29:52248 }
249
treib926ee2d2015-08-06 10:55:42250 ExtensionSystem::Get(profile_)->app_sorting()->FixNTPOrdinalCollisions();
[email protected]f8aefb132013-10-30 09:29:52251
252 return syncer::SyncError();
253}
254
treib0c714f7c2015-07-08 10:04:58255ExtensionSyncData ExtensionSyncService::CreateSyncData(
treib926ee2d2015-08-06 10:55:42256 const Extension& extension) const {
treib29e1b9b12015-11-11 08:50:56257 const std::string& id = extension.id();
treib35f9ed02015-08-10 10:38:44258 const ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile_);
treib29e1b9b12015-11-11 08:50:56259 int disable_reasons =
260 extension_prefs->GetDisableReasons(id) & kSyncableDisableReasons;
261 // Note that we're ignoring the enabled state during ApplySyncData (we check
262 // for the existence of disable reasons instead), we're just setting it here
263 // for older Chrome versions (<M48).
Minh X. Nguyen45479012017-08-18 21:35:36264 bool enabled = (disable_reasons == extensions::disable_reason::DISABLE_NONE);
proberge901ecab2017-08-31 19:24:28265 if (extension_prefs->GetExtensionBlacklistState(extension.id()) ==
266 extensions::BLACKLISTED_MALWARE) {
267 enabled = false;
268 NOTREACHED() << "Blacklisted extensions should not be getting synced.";
269 }
270
treib29e1b9b12015-11-11 08:50:56271 bool incognito_enabled = extensions::util::IsIncognitoEnabled(id, profile_);
Minh X. Nguyen45479012017-08-18 21:35:36272 bool remote_install = extension_prefs->HasDisableReason(
273 id, extensions::disable_reason::DISABLE_REMOTE_INSTALL);
treib0c714f7c2015-07-08 10:04:58274 ExtensionSyncData::OptionalBoolean allowed_on_all_url =
rdevlin.cronind01837b2016-08-17 01:37:18275 GetAllowedOnAllUrlsOptionalBoolean(extension, profile_);
mamir192d7882016-06-22 17:10:16276 bool installed_by_custodian =
277 extensions::util::WasInstalledByCustodian(id, profile_);
treibecc63c8d2015-09-07 16:34:47278 AppSorting* app_sorting = ExtensionSystem::Get(profile_)->app_sorting();
279
280 ExtensionSyncData result = extension.is_app()
281 ? ExtensionSyncData(
282 extension, enabled, disable_reasons, incognito_enabled,
283 remote_install, allowed_on_all_url,
mamir192d7882016-06-22 17:10:16284 installed_by_custodian,
treib29e1b9b12015-11-11 08:50:56285 app_sorting->GetAppLaunchOrdinal(id),
286 app_sorting->GetPageOrdinal(id),
287 extensions::GetLaunchTypePrefValue(extension_prefs, id))
treibecc63c8d2015-09-07 16:34:47288 : ExtensionSyncData(
289 extension, enabled, disable_reasons, incognito_enabled,
mamir192d7882016-06-22 17:10:16290 remote_install, allowed_on_all_url, installed_by_custodian);
treibecc63c8d2015-09-07 16:34:47291
292 // If there's a pending update, send the new version to sync instead of the
293 // installed one.
treib29e1b9b12015-11-11 08:50:56294 auto it = pending_updates_.find(id);
treib95e800c2015-10-13 15:48:31295 if (it != pending_updates_.end()) {
296 const base::Version& version = it->second.version;
treibecc63c8d2015-09-07 16:34:47297 // If we have a pending version, it should be newer than the installed one.
Devlin Cronin03bf2d22017-12-20 08:21:05298 DCHECK_EQ(-1, extension.version().CompareTo(version));
treibecc63c8d2015-09-07 16:34:47299 result.set_version(version);
treib95e800c2015-10-13 15:48:31300 // If we'll re-enable the extension once it's updated, also send that back
301 // to sync.
302 if (it->second.grant_permissions_and_reenable)
303 result.set_enabled(true);
treib0c714f7c2015-07-08 10:04:58304 }
treibecc63c8d2015-09-07 16:34:47305 return result;
[email protected]f8aefb132013-10-30 09:29:52306}
307
treibecc63c8d2015-09-07 16:34:47308void ExtensionSyncService::ApplySyncData(
rdevlin.cronind1aa8522015-02-13 00:25:57309 const ExtensionSyncData& extension_sync_data) {
rdevlin.cronin2f1ed4c2017-06-13 16:22:13310 const std::string& id = extension_sync_data.id();
311 // Note: |extension| may be null if it hasn't been installed yet.
312 const Extension* extension =
313 ExtensionRegistry::Get(profile_)->GetInstalledExtension(id);
314 // If there is an existing extension that shouldn't be sync'd, don't
315 // apply this sync data. This can happen if the local version of an
316 // extension is default-installed, but the sync server has data from another
317 // (non-default-installed) installation. We can't apply the sync data because
318 // it would always override the local state (which would never get sync'd).
319 // See crbug.com/731824.
320 if (extension && !ShouldSync(*extension))
321 return;
322
treibb794dd52015-12-01 18:47:14323 // Ignore any pref change notifications etc. while we're applying incoming
324 // sync data, so that we don't end up notifying ourselves.
325 base::AutoReset<bool> ignore_updates(&ignore_updates_, true);
326
mamir192d7882016-06-22 17:10:16327 // Note: this may cause an existing version of the extension to be reloaded.
328 extensions::util::SetWasInstalledByCustodian(
329 extension_sync_data.id(), profile_,
330 extension_sync_data.installed_by_custodian());
331
treibc3494532015-07-21 14:51:45332 syncer::ModelType type = extension_sync_data.is_app() ? syncer::APPS
333 : syncer::EXTENSIONS;
treib227b2582015-12-09 09:28:26334 SyncBundle* bundle = GetSyncBundle(type);
asargente48ab752016-03-12 00:59:20335 DCHECK(bundle->IsSyncing());
treib227b2582015-12-09 09:28:26336 if (extension && !IsCorrectSyncType(*extension, type)) {
asargente48ab752016-03-12 00:59:20337 // The installed item isn't the same type as the sync data item, so we need
338 // to remove the sync data item; otherwise it will be a zombie that will
339 // keep coming back even if the installed item with this id is uninstalled.
340 // First tell the bundle about the extension, so that it won't just ignore
341 // the deletion, then push the deletion.
342 bundle->ApplySyncData(extension_sync_data);
343 bundle->PushSyncDeletion(id, extension_sync_data.GetSyncData());
treibecc63c8d2015-09-07 16:34:47344 return;
treib227b2582015-12-09 09:28:26345 }
treibecc63c8d2015-09-07 16:34:47346
treibecc63c8d2015-09-07 16:34:47347 // Forward to the bundle. This will just update the list of synced extensions.
treibc3494532015-07-21 14:51:45348 bundle->ApplySyncData(extension_sync_data);
349
treibecc63c8d2015-09-07 16:34:47350 // Handle uninstalls first.
351 if (extension_sync_data.uninstalled()) {
Devlin Cronin6fd1cd62017-12-05 19:13:57352 base::string16 error;
353 bool uninstalled = true;
354 if (!extension) {
355 error = base::ASCIIToUTF16("Unknown extension");
356 uninstalled = false;
357 } else {
358 uninstalled = extension_service()->UninstallExtension(
359 id, extensions::UNINSTALL_REASON_SYNC, &error);
360 }
361
362 if (!uninstalled) {
363 LOG(WARNING) << "Failed to uninstall extension with id '" << id
364 << "' from sync: " << error;
treibecc63c8d2015-09-07 16:34:47365 }
366 return;
367 }
treib0c714f7c2015-07-08 10:04:58368
treibecc63c8d2015-09-07 16:34:47369 // Extension from sync was uninstalled by the user as an external extension.
370 // Honor user choice and skip installation/enabling.
371 // TODO(treib): Should we still apply pref changes?
treib8668a302015-10-05 16:21:53372 ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile_);
373 if (extension_prefs->IsExternalExtensionUninstalled(id)) {
treibecc63c8d2015-09-07 16:34:47374 LOG(WARNING) << "Extension with id " << id
375 << " from sync was uninstalled as external extension";
376 return;
377 }
378
treib29e1b9b12015-11-11 08:50:56379 enum {
380 NOT_INSTALLED,
381 INSTALLED_OUTDATED,
382 INSTALLED_MATCHING,
383 INSTALLED_NEWER,
384 } state = NOT_INSTALLED;
385 if (extension) {
Devlin Cronin03bf2d22017-12-20 08:21:05386 switch (extension->version().CompareTo(extension_sync_data.version())) {
treib29e1b9b12015-11-11 08:50:56387 case -1: state = INSTALLED_OUTDATED; break;
388 case 0: state = INSTALLED_MATCHING; break;
389 case 1: state = INSTALLED_NEWER; break;
390 default: NOTREACHED();
391 }
392 }
393
394 // Figure out the resulting set of disable reasons.
395 int disable_reasons = extension_prefs->GetDisableReasons(id);
396
397 // Chrome versions M37-M44 used |extension_sync_data.remote_install()| to tag
398 // not-yet-approved remote installs. It's redundant now that disable reasons
399 // are synced (DISABLE_REMOTE_INSTALL should be among them already), but some
400 // old sync data may still be around, and it doesn't hurt to add the reason.
401 // TODO(treib,devlin): Deprecate and eventually remove |remote_install|?
402 if (extension_sync_data.remote_install())
Minh X. Nguyen45479012017-08-18 21:35:36403 disable_reasons |= extensions::disable_reason::DISABLE_REMOTE_INSTALL;
treib29e1b9b12015-11-11 08:50:56404
405 // Add/remove disable reasons based on the incoming sync data.
406 int incoming_disable_reasons = extension_sync_data.disable_reasons();
407 if (!!incoming_disable_reasons == extension_sync_data.enabled()) {
408 // The enabled flag disagrees with the presence of disable reasons. This
treibb794dd52015-12-01 18:47:14409 // must either come from an old (<M45) client which doesn't sync disable
410 // reasons, or the extension is blacklisted (which doesn't have a
411 // corresponding disable reason).
treib29e1b9b12015-11-11 08:50:56412 // Update |disable_reasons| based on the enabled flag.
413 if (extension_sync_data.enabled())
414 disable_reasons &= ~kKnownSyncableDisableReasons;
415 else // Assume the extension was likely disabled by the user.
Minh X. Nguyen45479012017-08-18 21:35:36416 disable_reasons |= extensions::disable_reason::DISABLE_USER_ACTION;
treib29e1b9b12015-11-11 08:50:56417 } else {
418 // Replace the syncable disable reasons:
419 // 1. Remove any syncable disable reasons we might have.
420 disable_reasons &= ~kSyncableDisableReasons;
421 // 2. Add the incoming reasons. Mask with |kSyncableDisableReasons|, because
422 // Chrome M45-M47 also wrote local disable reasons to sync, and we don't
423 // want those.
424 disable_reasons |= incoming_disable_reasons & kSyncableDisableReasons;
425 }
treibecc63c8d2015-09-07 16:34:47426
427 // Enable/disable the extension.
Minh X. Nguyen45479012017-08-18 21:35:36428 bool should_be_enabled =
429 (disable_reasons == extensions::disable_reason::DISABLE_NONE);
treib95e800c2015-10-13 15:48:31430 bool reenable_after_update = false;
treib29e1b9b12015-11-11 08:50:56431 if (should_be_enabled && !extension_service()->IsExtensionEnabled(id)) {
treib8668a302015-10-05 16:21:53432 if (extension) {
433 // Only grant permissions if the sync data explicitly sets the disable
Minh X. Nguyen45479012017-08-18 21:35:36434 // reasons to extensions::disable_reason::DISABLE_NONE (as opposed to the
435 // legacy
436 // (<M45) case where they're not set at all), and if the version from sync
treib8668a302015-10-05 16:21:53437 // matches our local one.
438 bool grant_permissions = extension_sync_data.supports_disable_reasons() &&
treib29e1b9b12015-11-11 08:50:56439 (state == INSTALLED_MATCHING);
440 if (grant_permissions)
441 extension_service()->GrantPermissions(extension);
442
443 // Only enable if the extension has all required permissions.
444 // (Even if the version doesn't match - if the new version needs more
445 // permissions, it'll get disabled after the update.)
446 bool has_all_permissions =
447 grant_permissions ||
448 !extensions::PermissionMessageProvider::Get()->IsPrivilegeIncrease(
449 *extension_prefs->GetGrantedPermissions(id),
450 extension->permissions_data()->active_permissions(),
451 extension->GetType());
452 if (has_all_permissions)
453 extension_service()->EnableExtension(id);
454 else if (extension_sync_data.supports_disable_reasons())
455 reenable_after_update = true;
treib40d3ad92015-10-20 18:15:42456
brettw9e85ef42016-11-01 21:01:24457#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
treib29e1b9b12015-11-11 08:50:56458 if (!has_all_permissions && (state == INSTALLED_NEWER) &&
459 extensions::util::IsExtensionSupervised(extension, profile_)) {
460 SupervisedUserServiceFactory::GetForProfile(profile_)
Devlin Cronin03bf2d22017-12-20 08:21:05461 ->AddExtensionUpdateRequest(id, extension->version());
treib8668a302015-10-05 16:21:53462 }
treib29e1b9b12015-11-11 08:50:56463#endif
treib8668a302015-10-05 16:21:53464 } else {
465 // The extension is not installed yet. Set it to enabled; we'll check for
treib29e1b9b12015-11-11 08:50:56466 // permission increase (more accurately, for a version change) when it's
467 // actually installed.
treibecc63c8d2015-09-07 16:34:47468 extension_service()->EnableExtension(id);
treib8668a302015-10-05 16:21:53469 }
treib29e1b9b12015-11-11 08:50:56470 } else if (!should_be_enabled) {
471 // Note that |disable_reasons| includes any pre-existing reasons that
472 // weren't explicitly removed above.
473 if (extension_service()->IsExtensionEnabled(id))
474 extension_service()->DisableExtension(id, disable_reasons);
475 else // Already disabled, just replace the disable reasons.
476 extension_prefs->ReplaceDisableReasons(id, disable_reasons);
treibecc63c8d2015-09-07 16:34:47477 }
478
treibecc63c8d2015-09-07 16:34:47479 // Update the incognito flag.
480 extensions::util::SetIsIncognitoEnabled(
481 id, profile_, extension_sync_data.incognito_enabled());
482 extension = nullptr; // No longer safe to use.
483
484 // Update the all urls flag.
485 if (extension_sync_data.all_urls_enabled() !=
486 ExtensionSyncData::BOOLEAN_UNSET) {
487 bool allowed = extension_sync_data.all_urls_enabled() ==
rdevlin.cronind01837b2016-08-17 01:37:18488 ExtensionSyncData::BOOLEAN_TRUE;
489 extensions::ScriptingPermissionsModifier::SetAllowedOnAllUrlsForSync(
490 allowed, profile_, id);
treibecc63c8d2015-09-07 16:34:47491 }
492
493 // Set app-specific data.
treib0c714f7c2015-07-08 10:04:58494 if (extension_sync_data.is_app()) {
495 if (extension_sync_data.app_launch_ordinal().IsValid() &&
496 extension_sync_data.page_ordinal().IsValid()) {
treib926ee2d2015-08-06 10:55:42497 AppSorting* app_sorting = ExtensionSystem::Get(profile_)->app_sorting();
498 app_sorting->SetAppLaunchOrdinal(
treib0c714f7c2015-07-08 10:04:58499 id,
500 extension_sync_data.app_launch_ordinal());
treib926ee2d2015-08-06 10:55:42501 app_sorting->SetPageOrdinal(id, extension_sync_data.page_ordinal());
treib0c714f7c2015-07-08 10:04:58502 }
503
treibc3494532015-07-21 14:51:45504 // The corresponding validation of this value during ExtensionSyncData
505 // population is in ExtensionSyncData::ToAppSpecifics.
treib0c714f7c2015-07-08 10:04:58506 if (extension_sync_data.launch_type() >= extensions::LAUNCH_TYPE_FIRST &&
507 extension_sync_data.launch_type() < extensions::NUM_LAUNCH_TYPES) {
508 extensions::SetLaunchType(
509 profile_, id, extension_sync_data.launch_type());
510 }
511
Giovanni Ortuño Urquidib7d775d2017-07-18 03:20:16512 if (!extension_sync_data.bookmark_app_url().empty()) {
513 // Handles creating and updating the bookmark app.
treib0c714f7c2015-07-08 10:04:58514 ApplyBookmarkAppSyncData(extension_sync_data);
Giovanni Ortuño Urquidib7d775d2017-07-18 03:20:16515 return;
516 }
treib0c714f7c2015-07-08 10:04:58517 }
518
treibecc63c8d2015-09-07 16:34:47519 // Finally, trigger installation/update as required.
520 bool check_for_updates = false;
treib29e1b9b12015-11-11 08:50:56521 if (state == INSTALLED_OUTDATED) {
treibecc63c8d2015-09-07 16:34:47522 // If the extension is installed but outdated, store the new version.
treib29e1b9b12015-11-11 08:50:56523 pending_updates_[id] =
524 PendingUpdate(extension_sync_data.version(), reenable_after_update);
525 check_for_updates = true;
526 } else if (state == NOT_INSTALLED) {
treibecc63c8d2015-09-07 16:34:47527 if (!extension_service()->pending_extension_manager()->AddFromSync(
528 id,
529 extension_sync_data.update_url(),
treibe960e282015-09-11 10:38:08530 extension_sync_data.version(),
treib227b2582015-12-09 09:28:26531 ShouldAllowInstall,
mamir192d7882016-06-22 17:10:16532 extension_sync_data.remote_install())) {
treibecc63c8d2015-09-07 16:34:47533 LOG(WARNING) << "Could not add pending extension for " << id;
534 // This means that the extension is already pending installation, with a
535 // non-INTERNAL location. Add to pending_sync_data, even though it will
536 // never be removed (we'll never install a syncable version of the
537 // extension), so that GetAllSyncData() continues to send it.
538 }
539 // Track pending extensions so that we can return them in GetAllSyncData().
treib227b2582015-12-09 09:28:26540 bundle->AddPendingExtensionData(extension_sync_data);
treibecc63c8d2015-09-07 16:34:47541 check_for_updates = true;
[email protected]f8aefb132013-10-30 09:29:52542 }
543
treibecc63c8d2015-09-07 16:34:47544 if (check_for_updates)
545 extension_service()->CheckForUpdatesSoon();
[email protected]f8aefb132013-10-30 09:29:52546}
547
treib0c714f7c2015-07-08 10:04:58548void ExtensionSyncService::ApplyBookmarkAppSyncData(
treib926ee2d2015-08-06 10:55:42549 const ExtensionSyncData& extension_sync_data) {
treib0c714f7c2015-07-08 10:04:58550 DCHECK(extension_sync_data.is_app());
[email protected]f8aefb132013-10-30 09:29:52551
[email protected]d93fcd5132014-04-24 08:42:45552 // Process bookmark app sync if necessary.
treib0c714f7c2015-07-08 10:04:58553 GURL bookmark_app_url(extension_sync_data.bookmark_app_url());
[email protected]d93fcd5132014-04-24 08:42:45554 if (!bookmark_app_url.is_valid() ||
treib0c714f7c2015-07-08 10:04:58555 extension_sync_data.uninstalled()) {
[email protected]d93fcd5132014-04-24 08:42:45556 return;
557 }
558
treib0c714f7c2015-07-08 10:04:58559 const Extension* extension =
treib35f9ed02015-08-10 10:38:44560 extension_service()->GetInstalledExtension(extension_sync_data.id());
[email protected]d93fcd5132014-04-24 08:42:45561
562 // Return if there are no bookmark app details that need updating.
treib0c714f7c2015-07-08 10:04:58563 if (extension &&
564 extension->non_localized_name() == extension_sync_data.name() &&
565 extension->description() ==
566 extension_sync_data.bookmark_app_description()) {
[email protected]d93fcd5132014-04-24 08:42:45567 return;
568 }
569
570 WebApplicationInfo web_app_info;
571 web_app_info.app_url = bookmark_app_url;
treib0c714f7c2015-07-08 10:04:58572 web_app_info.title = base::UTF8ToUTF16(extension_sync_data.name());
[email protected]d93fcd5132014-04-24 08:42:45573 web_app_info.description =
treib0c714f7c2015-07-08 10:04:58574 base::UTF8ToUTF16(extension_sync_data.bookmark_app_description());
Giovanni Ortuño Urquidie7e79d452017-08-03 10:16:15575 web_app_info.scope = GURL(extension_sync_data.bookmark_app_scope());
Christopher Lamcec8c4f2017-10-16 01:38:43576 web_app_info.theme_color = extension_sync_data.bookmark_app_theme_color();
treib0c714f7c2015-07-08 10:04:58577 if (!extension_sync_data.bookmark_app_icon_color().empty()) {
limasdf3aa01fa2015-11-01 19:39:14578 extensions::image_util::ParseHexColorString(
treib0c714f7c2015-07-08 10:04:58579 extension_sync_data.bookmark_app_icon_color(),
benwellsd4b64a72014-12-11 12:38:27580 &web_app_info.generated_icon_color);
581 }
treib0c714f7c2015-07-08 10:04:58582 for (const auto& icon : extension_sync_data.linked_icons()) {
benwellsed75d1b52015-04-29 02:39:19583 WebApplicationInfo::IconInfo icon_info;
584 icon_info.url = icon.url;
585 icon_info.width = icon.size;
586 icon_info.height = icon.size;
587 web_app_info.icons.push_back(icon_info);
588 }
[email protected]d93fcd5132014-04-24 08:42:45589
Christopher Lam780f0e12017-10-09 11:01:16590 CreateOrUpdateBookmarkApp(extension_service(), &web_app_info);
[email protected]d93fcd5132014-04-24 08:42:45591}
592
treib35f9ed02015-08-10 10:38:44593void ExtensionSyncService::SetSyncStartFlareForTesting(
[email protected]f8aefb132013-10-30 09:29:52594 const syncer::SyncableService::StartSyncFlare& flare) {
595 flare_ = flare;
596}
597
treib227b2582015-12-09 09:28:26598void ExtensionSyncService::DeleteThemeDoNotUse(const Extension& theme) {
599 DCHECK(theme.is_theme());
600 GetSyncBundle(syncer::EXTENSIONS)->PushSyncDeletion(
601 theme.id(), CreateSyncData(theme).GetSyncData());
602}
603
treib35f9ed02015-08-10 10:38:44604ExtensionService* ExtensionSyncService::extension_service() const {
605 return ExtensionSystem::Get(profile_)->extension_service();
606}
607
treib8a6d9892015-08-26 10:23:19608void ExtensionSyncService::OnExtensionInstalled(
609 content::BrowserContext* browser_context,
treibecc63c8d2015-09-07 16:34:47610 const Extension* extension,
treib8a6d9892015-08-26 10:23:19611 bool is_update) {
612 DCHECK_EQ(profile_, browser_context);
treibecc63c8d2015-09-07 16:34:47613 // Clear pending version if the installed one has caught up.
treib95e800c2015-10-13 15:48:31614 auto it = pending_updates_.find(extension->id());
615 if (it != pending_updates_.end()) {
Devlin Cronin03bf2d22017-12-20 08:21:05616 int compare_result = extension->version().CompareTo(it->second.version);
asargente48ab752016-03-12 00:59:20617 if (compare_result == 0 && it->second.grant_permissions_and_reenable) {
618 // The call to SyncExtensionChangeIfNeeded below will take care of syncing
619 // changes to this extension, so we don't want to trigger sync activity
620 // from the call to GrantPermissionsAndEnableExtension.
621 base::AutoReset<bool> ignore_updates(&ignore_updates_, true);
treib95e800c2015-10-13 15:48:31622 extension_service()->GrantPermissionsAndEnableExtension(extension);
asargente48ab752016-03-12 00:59:20623 }
treib95e800c2015-10-13 15:48:31624 if (compare_result >= 0)
625 pending_updates_.erase(it);
treibecc63c8d2015-09-07 16:34:47626 }
treib8a6d9892015-08-26 10:23:19627 SyncExtensionChangeIfNeeded(*extension);
628}
629
630void ExtensionSyncService::OnExtensionUninstalled(
631 content::BrowserContext* browser_context,
treibecc63c8d2015-09-07 16:34:47632 const Extension* extension,
treib8a6d9892015-08-26 10:23:19633 extensions::UninstallReason reason) {
634 DCHECK_EQ(profile_, browser_context);
635 // Don't bother syncing if the extension will be re-installed momentarily.
636 if (reason == extensions::UNINSTALL_REASON_REINSTALL ||
treib227b2582015-12-09 09:28:26637 !ShouldSync(*extension)) {
treib8a6d9892015-08-26 10:23:19638 return;
treib227b2582015-12-09 09:28:26639 }
treib8a6d9892015-08-26 10:23:19640
641 // TODO(tim): If we get here and IsSyncing is false, this will cause
642 // "back from the dead" style bugs, because sync will add-back the extension
643 // that was uninstalled here when MergeDataAndStartSyncing is called.
644 // See crbug.com/256795.
645 // Possible fix: Set NeedsSync here, then in MergeDataAndStartSyncing, if
646 // NeedsSync is set but the extension isn't installed, send a sync deletion.
treibb794dd52015-12-01 18:47:14647 if (!ignore_updates_) {
648 syncer::ModelType type =
649 extension->is_app() ? syncer::APPS : syncer::EXTENSIONS;
650 SyncBundle* bundle = GetSyncBundle(type);
651 if (bundle->IsSyncing()) {
652 bundle->PushSyncDeletion(extension->id(),
653 CreateSyncData(*extension).GetSyncData());
654 } else if (extension_service()->is_ready() && !flare_.is_null()) {
655 flare_.Run(type); // Tell sync to start ASAP.
656 }
treib8a6d9892015-08-26 10:23:19657 }
treibecc63c8d2015-09-07 16:34:47658
treib95e800c2015-10-13 15:48:31659 pending_updates_.erase(extension->id());
treib8a6d9892015-08-26 10:23:19660}
661
662void ExtensionSyncService::OnExtensionStateChanged(
663 const std::string& extension_id,
664 bool state) {
665 ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
666 const Extension* extension = registry->GetInstalledExtension(extension_id);
667 // We can get pref change notifications for extensions that aren't installed
668 // (yet). In that case, we'll pick up the change later via ExtensionRegistry
669 // observation (in OnExtensionInstalled).
670 if (extension)
671 SyncExtensionChangeIfNeeded(*extension);
672}
673
674void ExtensionSyncService::OnExtensionDisableReasonsChanged(
675 const std::string& extension_id,
676 int disabled_reasons) {
677 ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
678 const Extension* extension = registry->GetInstalledExtension(extension_id);
679 // We can get pref change notifications for extensions that aren't installed
680 // (yet). In that case, we'll pick up the change later via ExtensionRegistry
681 // observation (in OnExtensionInstalled).
682 if (extension)
683 SyncExtensionChangeIfNeeded(*extension);
684}
685
treib0c714f7c2015-07-08 10:04:58686SyncBundle* ExtensionSyncService::GetSyncBundle(syncer::ModelType type) {
687 return const_cast<SyncBundle*>(
688 const_cast<const ExtensionSyncService&>(*this).GetSyncBundle(type));
689}
690
691const SyncBundle* ExtensionSyncService::GetSyncBundle(
692 syncer::ModelType type) const {
treibc644a1c2015-07-13 08:37:04693 return (type == syncer::APPS) ? &app_sync_bundle_ : &extension_sync_bundle_;
694}
695
treibc3494532015-07-21 14:51:45696std::vector<ExtensionSyncData> ExtensionSyncService::GetLocalSyncDataList(
treib0feb8282016-01-18 10:18:15697 syncer::ModelType type) const {
treibecc63c8d2015-09-07 16:34:47698 // Collect the local state.
treib0c714f7c2015-07-08 10:04:58699 ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
treibc3494532015-07-21 14:51:45700 std::vector<ExtensionSyncData> data;
701 // TODO(treib, kalman): Should we be including blacklisted/blocked extensions
702 // here? I.e. just calling registry->GeneratedInstalledExtensionsSet()?
703 // It would be more consistent, but the danger is that the black/blocklist
704 // hasn't been updated on all clients by the time sync has kicked in -
705 // so it's safest not to. Take care to add any other extension lists here
706 // in the future if they are added.
treib0feb8282016-01-18 10:18:15707 FillSyncDataList(registry->enabled_extensions(), type, &data);
708 FillSyncDataList(registry->disabled_extensions(), type, &data);
709 FillSyncDataList(registry->terminated_extensions(), type, &data);
treibc3494532015-07-21 14:51:45710 return data;
treib0c714f7c2015-07-08 10:04:58711}
712
713void ExtensionSyncService::FillSyncDataList(
714 const ExtensionSet& extensions,
715 syncer::ModelType type,
716 std::vector<ExtensionSyncData>* sync_data_list) const {
treib0c714f7c2015-07-08 10:04:58717 for (const scoped_refptr<const Extension>& extension : extensions) {
treib0feb8282016-01-18 10:18:15718 if (IsCorrectSyncType(*extension, type) && ShouldSync(*extension)) {
treibecc63c8d2015-09-07 16:34:47719 // We should never have pending data for an installed extension.
720 DCHECK(!GetSyncBundle(type)->HasPendingExtensionData(extension->id()));
treib0c714f7c2015-07-08 10:04:58721 sync_data_list->push_back(CreateSyncData(*extension));
722 }
723 }
724}
treib227b2582015-12-09 09:28:26725
726bool ExtensionSyncService::ShouldSync(const Extension& extension) const {
727 // Themes are handled by the ThemeSyncableService.
728 return extensions::util::ShouldSync(&extension, profile_) &&
729 !extension.is_theme();
730}