blob: fd538cbd4cc248f2748c0ebd1032ca057fa50d63 [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
54void OnWebApplicationInfoLoaded(
55 WebApplicationInfo synced_info,
56 base::WeakPtr<ExtensionService> extension_service,
57 const WebApplicationInfo& loaded_info) {
58 DCHECK_EQ(synced_info.app_url, loaded_info.app_url);
59
60 if (!extension_service)
61 return;
62
63 // Use the old icons if they exist.
64 synced_info.icons = loaded_info.icons;
benwellsd4b64a72014-12-11 12:38:2765 CreateOrUpdateBookmarkApp(extension_service.get(), &synced_info);
[email protected]d93fcd5132014-04-24 08:42:4566}
67
rdevlin.cronind1aa8522015-02-13 00:25:5768// Returns the pref value for "all urls enabled" for the given extension id.
69ExtensionSyncData::OptionalBoolean GetAllowedOnAllUrlsOptionalBoolean(
rdevlin.cronind01837b2016-08-17 01:37:1870 const Extension& extension,
rdevlin.cronind1aa8522015-02-13 00:25:5771 content::BrowserContext* context) {
rdevlin.cronind01837b2016-08-17 01:37:1872 extensions::ScriptingPermissionsModifier permissions_modifier(context,
73 &extension);
74 bool allowed_on_all_urls = permissions_modifier.IsAllowedOnAllUrls();
rdevlin.cronind1aa8522015-02-13 00:25:5775 // If the extension is not allowed on all urls (which is not the default),
76 // then we have to sync the preference.
77 if (!allowed_on_all_urls)
78 return ExtensionSyncData::BOOLEAN_FALSE;
79
80 // If the user has explicitly set a value, then we sync it.
rdevlin.cronind01837b2016-08-17 01:37:1881 if (permissions_modifier.HasSetAllowedOnAllUrls())
rdevlin.cronind1aa8522015-02-13 00:25:5782 return ExtensionSyncData::BOOLEAN_TRUE;
83
84 // Otherwise, unset.
85 return ExtensionSyncData::BOOLEAN_UNSET;
86}
87
treib0c714f7c2015-07-08 10:04:5888// Returns true if the sync type of |extension| matches |type|.
89bool IsCorrectSyncType(const Extension& extension, syncer::ModelType type) {
treibc644a1c2015-07-13 08:37:0490 return (type == syncer::EXTENSIONS && extension.is_extension()) ||
91 (type == syncer::APPS && extension.is_app());
treib0c714f7c2015-07-08 10:04:5892}
93
treib227b2582015-12-09 09:28:2694// Predicate for PendingExtensionManager.
95// TODO(treib,devlin): The !is_theme check shouldn't be necessary anymore after
96// all the bad data from crbug.com/558299 has been cleaned up, after M52 or so.
97bool ShouldAllowInstall(const Extension* extension) {
98 return !extension->is_theme() &&
99 extensions::sync_helper::IsSyncable(extension);
100}
101
treibc3494532015-07-21 14:51:45102syncer::SyncDataList ToSyncerSyncDataList(
103 const std::vector<ExtensionSyncData>& data) {
104 syncer::SyncDataList result;
105 result.reserve(data.size());
106 for (const ExtensionSyncData& item : data)
107 result.push_back(item.GetSyncData());
108 return result;
109}
110
mamire9609642016-06-28 22:17:54111static_assert(Extension::DISABLE_REASON_LAST == (1 << 16),
treib29e1b9b12015-11-11 08:50:56112 "Please consider whether your new disable reason should be"
113 " syncable, and if so update this bitmask accordingly!");
114const int kKnownSyncableDisableReasons =
115 Extension::DISABLE_USER_ACTION |
116 Extension::DISABLE_PERMISSIONS_INCREASE |
117 Extension::DISABLE_SIDELOAD_WIPEOUT |
118 Extension::DISABLE_GREYLIST |
119 Extension::DISABLE_REMOTE_INSTALL;
120const int kAllKnownDisableReasons = Extension::DISABLE_REASON_LAST - 1;
121// We also include any future bits for newer clients that added another disable
122// reason.
123const int kSyncableDisableReasons =
124 kKnownSyncableDisableReasons | ~kAllKnownDisableReasons;
125
[email protected]d93fcd5132014-04-24 08:42:45126} // namespace
127
treib95e800c2015-10-13 15:48:31128struct ExtensionSyncService::PendingUpdate {
129 PendingUpdate() : grant_permissions_and_reenable(false) {}
130 PendingUpdate(const base::Version& version,
131 bool grant_permissions_and_reenable)
132 : version(version),
133 grant_permissions_and_reenable(grant_permissions_and_reenable) {}
134
135 base::Version version;
136 bool grant_permissions_and_reenable;
137};
138
treib35f9ed02015-08-10 10:38:44139ExtensionSyncService::ExtensionSyncService(Profile* profile)
[email protected]f8aefb132013-10-30 09:29:52140 : profile_(profile),
treib8a6d9892015-08-26 10:23:19141 registry_observer_(this),
142 prefs_observer_(this),
treibb794dd52015-12-01 18:47:14143 ignore_updates_(false),
treib35f9ed02015-08-10 10:38:44144 flare_(sync_start_util::GetFlareForSyncableService(profile->GetPath())) {
treib8a6d9892015-08-26 10:23:19145 registry_observer_.Add(ExtensionRegistry::Get(profile_));
146 prefs_observer_.Add(ExtensionPrefs::Get(profile_));
[email protected]f8aefb132013-10-30 09:29:52147}
148
treib8a6d9892015-08-26 10:23:19149ExtensionSyncService::~ExtensionSyncService() {
150}
[email protected]f8aefb132013-10-30 09:29:52151
152// static
macourteau86b29d82015-01-23 13:24:45153ExtensionSyncService* ExtensionSyncService::Get(
154 content::BrowserContext* context) {
155 return ExtensionSyncServiceFactory::GetForBrowserContext(context);
[email protected]f8aefb132013-10-30 09:29:52156}
157
treib0c714f7c2015-07-08 10:04:58158void ExtensionSyncService::SyncExtensionChangeIfNeeded(
159 const Extension& extension) {
treib227b2582015-12-09 09:28:26160 if (ignore_updates_ || !ShouldSync(extension))
treib0c714f7c2015-07-08 10:04:58161 return;
162
163 syncer::ModelType type =
164 extension.is_app() ? syncer::APPS : syncer::EXTENSIONS;
165 SyncBundle* bundle = GetSyncBundle(type);
treibc3494532015-07-21 14:51:45166 if (bundle->IsSyncing()) {
167 bundle->PushSyncAddOrUpdate(extension.id(),
168 CreateSyncData(extension).GetSyncData());
169 DCHECK(!ExtensionPrefs::Get(profile_)->NeedsSync(extension.id()));
170 } else {
171 ExtensionPrefs::Get(profile_)->SetNeedsSync(extension.id(), true);
treib35f9ed02015-08-10 10:38:44172 if (extension_service()->is_ready() && !flare_.is_null())
treibc3494532015-07-21 14:51:45173 flare_.Run(type); // Tell sync to start ASAP.
174 }
treib0c714f7c2015-07-08 10:04:58175}
176
treib40d3ad92015-10-20 18:15:42177bool ExtensionSyncService::HasPendingReenable(
178 const std::string& id,
179 const base::Version& version) const {
180 auto it = pending_updates_.find(id);
181 if (it == pending_updates_.end())
182 return false;
183 const PendingUpdate& pending = it->second;
robpercivaldcd8b102016-01-25 19:39:00184 return pending.version == version &&
treib40d3ad92015-10-20 18:15:42185 pending.grant_permissions_and_reenable;
186}
187
[email protected]f8aefb132013-10-30 09:29:52188syncer::SyncMergeResult ExtensionSyncService::MergeDataAndStartSyncing(
189 syncer::ModelType type,
190 const syncer::SyncDataList& initial_sync_data,
dchengc963c7142016-04-08 03:55:22191 std::unique_ptr<syncer::SyncChangeProcessor> sync_processor,
192 std::unique_ptr<syncer::SyncErrorFactory> sync_error_factory) {
[email protected]f8aefb132013-10-30 09:29:52193 CHECK(sync_processor.get());
treib0c714f7c2015-07-08 10:04:58194 LOG_IF(FATAL, type != syncer::EXTENSIONS && type != syncer::APPS)
195 << "Got " << type << " ModelType";
[email protected]f8aefb132013-10-30 09:29:52196
treib0c714f7c2015-07-08 10:04:58197 SyncBundle* bundle = GetSyncBundle(type);
dcheng1fc00f12015-12-26 22:18:03198 bundle->StartSyncing(std::move(sync_processor));
[email protected]f8aefb132013-10-30 09:29:52199
treibc3494532015-07-21 14:51:45200 // Apply the initial sync data, filtering out any items where we have more
201 // recent local changes. Also tell the SyncBundle the extension IDs.
202 for (const syncer::SyncData& sync_data : initial_sync_data) {
dchengc963c7142016-04-08 03:55:22203 std::unique_ptr<ExtensionSyncData> extension_sync_data(
treibc3494532015-07-21 14:51:45204 ExtensionSyncData::CreateFromSyncData(sync_data));
205 // If the extension has local state that needs to be synced, ignore this
206 // change (we assume the local state is more recent).
207 if (extension_sync_data &&
208 !ExtensionPrefs::Get(profile_)->NeedsSync(extension_sync_data->id())) {
209 ApplySyncData(*extension_sync_data);
210 }
211 }
[email protected]f8aefb132013-10-30 09:29:52212
treib0feb8282016-01-18 10:18:15213 // Now push the local state to sync.
214 // Note: We'd like to only send out changes for extensions which have
215 // NeedsSync set. However, we can't tell if our changes ever made it to the
216 // sync server (they might not e.g. when there's a temporary auth error), so
217 // we couldn't safely clear the flag. So just send out everything and let the
218 // sync client handle no-op changes.
219 std::vector<ExtensionSyncData> data_list = GetLocalSyncDataList(type);
treibc3494532015-07-21 14:51:45220 bundle->PushSyncDataList(ToSyncerSyncDataList(data_list));
treib0feb8282016-01-18 10:18:15221
treibc3494532015-07-21 14:51:45222 for (const ExtensionSyncData& data : data_list)
223 ExtensionPrefs::Get(profile_)->SetNeedsSync(data.id(), false);
[email protected]f8aefb132013-10-30 09:29:52224
treibc3494532015-07-21 14:51:45225 if (type == syncer::APPS)
treib926ee2d2015-08-06 10:55:42226 ExtensionSystem::Get(profile_)->app_sorting()->FixNTPOrdinalCollisions();
[email protected]f8aefb132013-10-30 09:29:52227
228 return syncer::SyncMergeResult(type);
229}
230
231void ExtensionSyncService::StopSyncing(syncer::ModelType type) {
treib0c714f7c2015-07-08 10:04:58232 GetSyncBundle(type)->Reset();
[email protected]f8aefb132013-10-30 09:29:52233}
234
235syncer::SyncDataList ExtensionSyncService::GetAllSyncData(
236 syncer::ModelType type) const {
treibc3494532015-07-21 14:51:45237 const SyncBundle* bundle = GetSyncBundle(type);
238 if (!bundle->IsSyncing())
239 return syncer::SyncDataList();
240
treib0feb8282016-01-18 10:18:15241 std::vector<ExtensionSyncData> sync_data_list = GetLocalSyncDataList(type);
treibc3494532015-07-21 14:51:45242
treibecc63c8d2015-09-07 16:34:47243 // Add pending data (where the local extension is not installed yet).
244 std::vector<ExtensionSyncData> pending_extensions =
245 bundle->GetPendingExtensionData();
treibc3494532015-07-21 14:51:45246 sync_data_list.insert(sync_data_list.begin(),
247 pending_extensions.begin(),
248 pending_extensions.end());
249
250 return ToSyncerSyncDataList(sync_data_list);
[email protected]f8aefb132013-10-30 09:29:52251}
252
253syncer::SyncError ExtensionSyncService::ProcessSyncChanges(
254 const tracked_objects::Location& from_here,
255 const syncer::SyncChangeList& change_list) {
treib0c714f7c2015-07-08 10:04:58256 for (const syncer::SyncChange& sync_change : change_list) {
dchengc963c7142016-04-08 03:55:22257 std::unique_ptr<ExtensionSyncData> extension_sync_data(
treibc3494532015-07-21 14:51:45258 ExtensionSyncData::CreateFromSyncChange(sync_change));
259 if (extension_sync_data)
260 ApplySyncData(*extension_sync_data);
[email protected]f8aefb132013-10-30 09:29:52261 }
262
treib926ee2d2015-08-06 10:55:42263 ExtensionSystem::Get(profile_)->app_sorting()->FixNTPOrdinalCollisions();
[email protected]f8aefb132013-10-30 09:29:52264
265 return syncer::SyncError();
266}
267
treib0c714f7c2015-07-08 10:04:58268ExtensionSyncData ExtensionSyncService::CreateSyncData(
treib926ee2d2015-08-06 10:55:42269 const Extension& extension) const {
treib29e1b9b12015-11-11 08:50:56270 const std::string& id = extension.id();
treib35f9ed02015-08-10 10:38:44271 const ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile_);
treib29e1b9b12015-11-11 08:50:56272 int disable_reasons =
273 extension_prefs->GetDisableReasons(id) & kSyncableDisableReasons;
274 // Note that we're ignoring the enabled state during ApplySyncData (we check
275 // for the existence of disable reasons instead), we're just setting it here
276 // for older Chrome versions (<M48).
277 bool enabled = (disable_reasons == Extension::DISABLE_NONE);
treib8a6d9892015-08-26 10:23:19278 enabled = enabled &&
279 extension_prefs->GetExtensionBlacklistState(extension.id()) ==
280 extensions::NOT_BLACKLISTED;
treib29e1b9b12015-11-11 08:50:56281 bool incognito_enabled = extensions::util::IsIncognitoEnabled(id, profile_);
treib0c714f7c2015-07-08 10:04:58282 bool remote_install =
treib29e1b9b12015-11-11 08:50:56283 extension_prefs->HasDisableReason(id, Extension::DISABLE_REMOTE_INSTALL);
treib0c714f7c2015-07-08 10:04:58284 ExtensionSyncData::OptionalBoolean allowed_on_all_url =
rdevlin.cronind01837b2016-08-17 01:37:18285 GetAllowedOnAllUrlsOptionalBoolean(extension, profile_);
mamir192d7882016-06-22 17:10:16286 bool installed_by_custodian =
287 extensions::util::WasInstalledByCustodian(id, profile_);
treibecc63c8d2015-09-07 16:34:47288 AppSorting* app_sorting = ExtensionSystem::Get(profile_)->app_sorting();
289
290 ExtensionSyncData result = extension.is_app()
291 ? ExtensionSyncData(
292 extension, enabled, disable_reasons, incognito_enabled,
293 remote_install, allowed_on_all_url,
mamir192d7882016-06-22 17:10:16294 installed_by_custodian,
treib29e1b9b12015-11-11 08:50:56295 app_sorting->GetAppLaunchOrdinal(id),
296 app_sorting->GetPageOrdinal(id),
297 extensions::GetLaunchTypePrefValue(extension_prefs, id))
treibecc63c8d2015-09-07 16:34:47298 : ExtensionSyncData(
299 extension, enabled, disable_reasons, incognito_enabled,
mamir192d7882016-06-22 17:10:16300 remote_install, allowed_on_all_url, installed_by_custodian);
treibecc63c8d2015-09-07 16:34:47301
302 // If there's a pending update, send the new version to sync instead of the
303 // installed one.
treib29e1b9b12015-11-11 08:50:56304 auto it = pending_updates_.find(id);
treib95e800c2015-10-13 15:48:31305 if (it != pending_updates_.end()) {
306 const base::Version& version = it->second.version;
treibecc63c8d2015-09-07 16:34:47307 // If we have a pending version, it should be newer than the installed one.
308 DCHECK_EQ(-1, extension.version()->CompareTo(version));
309 result.set_version(version);
treib95e800c2015-10-13 15:48:31310 // If we'll re-enable the extension once it's updated, also send that back
311 // to sync.
312 if (it->second.grant_permissions_and_reenable)
313 result.set_enabled(true);
treib0c714f7c2015-07-08 10:04:58314 }
treibecc63c8d2015-09-07 16:34:47315 return result;
[email protected]f8aefb132013-10-30 09:29:52316}
317
treibecc63c8d2015-09-07 16:34:47318void ExtensionSyncService::ApplySyncData(
rdevlin.cronind1aa8522015-02-13 00:25:57319 const ExtensionSyncData& extension_sync_data) {
rdevlin.cronin2f1ed4c2017-06-13 16:22:13320 const std::string& id = extension_sync_data.id();
321 // Note: |extension| may be null if it hasn't been installed yet.
322 const Extension* extension =
323 ExtensionRegistry::Get(profile_)->GetInstalledExtension(id);
324 // If there is an existing extension that shouldn't be sync'd, don't
325 // apply this sync data. This can happen if the local version of an
326 // extension is default-installed, but the sync server has data from another
327 // (non-default-installed) installation. We can't apply the sync data because
328 // it would always override the local state (which would never get sync'd).
329 // See crbug.com/731824.
330 if (extension && !ShouldSync(*extension))
331 return;
332
treibb794dd52015-12-01 18:47:14333 // Ignore any pref change notifications etc. while we're applying incoming
334 // sync data, so that we don't end up notifying ourselves.
335 base::AutoReset<bool> ignore_updates(&ignore_updates_, true);
336
mamir192d7882016-06-22 17:10:16337 // Note: this may cause an existing version of the extension to be reloaded.
338 extensions::util::SetWasInstalledByCustodian(
339 extension_sync_data.id(), profile_,
340 extension_sync_data.installed_by_custodian());
341
treibc3494532015-07-21 14:51:45342 syncer::ModelType type = extension_sync_data.is_app() ? syncer::APPS
343 : syncer::EXTENSIONS;
treib227b2582015-12-09 09:28:26344 SyncBundle* bundle = GetSyncBundle(type);
asargente48ab752016-03-12 00:59:20345 DCHECK(bundle->IsSyncing());
treib227b2582015-12-09 09:28:26346 if (extension && !IsCorrectSyncType(*extension, type)) {
asargente48ab752016-03-12 00:59:20347 // The installed item isn't the same type as the sync data item, so we need
348 // to remove the sync data item; otherwise it will be a zombie that will
349 // keep coming back even if the installed item with this id is uninstalled.
350 // First tell the bundle about the extension, so that it won't just ignore
351 // the deletion, then push the deletion.
352 bundle->ApplySyncData(extension_sync_data);
353 bundle->PushSyncDeletion(id, extension_sync_data.GetSyncData());
treibecc63c8d2015-09-07 16:34:47354 return;
treib227b2582015-12-09 09:28:26355 }
treibecc63c8d2015-09-07 16:34:47356
treibecc63c8d2015-09-07 16:34:47357 // Forward to the bundle. This will just update the list of synced extensions.
treibc3494532015-07-21 14:51:45358 bundle->ApplySyncData(extension_sync_data);
359
treibecc63c8d2015-09-07 16:34:47360 // Handle uninstalls first.
361 if (extension_sync_data.uninstalled()) {
362 if (!ExtensionService::UninstallExtensionHelper(
363 extension_service(), id, extensions::UNINSTALL_REASON_SYNC)) {
364 LOG(WARNING) << "Could not uninstall extension " << id << " for sync";
365 }
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) {
386 switch (extension->version()->CompareTo(extension_sync_data.version())) {
387 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())
403 disable_reasons |= Extension::DISABLE_REMOTE_INSTALL;
404
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.
416 disable_reasons |= Extension::DISABLE_USER_ACTION;
417 } 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.
treib29e1b9b12015-11-11 08:50:56428 bool should_be_enabled = (disable_reasons == Extension::DISABLE_NONE);
treib95e800c2015-10-13 15:48:31429 bool reenable_after_update = false;
treib29e1b9b12015-11-11 08:50:56430 if (should_be_enabled && !extension_service()->IsExtensionEnabled(id)) {
treib8668a302015-10-05 16:21:53431 if (extension) {
432 // Only grant permissions if the sync data explicitly sets the disable
433 // reasons to Extension::DISABLE_NONE (as opposed to the legacy (<M45)
434 // case where they're not set at all), and if the version from sync
435 // matches our local one.
436 bool grant_permissions = extension_sync_data.supports_disable_reasons() &&
treib29e1b9b12015-11-11 08:50:56437 (state == INSTALLED_MATCHING);
438 if (grant_permissions)
439 extension_service()->GrantPermissions(extension);
440
441 // Only enable if the extension has all required permissions.
442 // (Even if the version doesn't match - if the new version needs more
443 // permissions, it'll get disabled after the update.)
444 bool has_all_permissions =
445 grant_permissions ||
446 !extensions::PermissionMessageProvider::Get()->IsPrivilegeIncrease(
447 *extension_prefs->GetGrantedPermissions(id),
448 extension->permissions_data()->active_permissions(),
449 extension->GetType());
450 if (has_all_permissions)
451 extension_service()->EnableExtension(id);
452 else if (extension_sync_data.supports_disable_reasons())
453 reenable_after_update = true;
treib40d3ad92015-10-20 18:15:42454
brettw9e85ef42016-11-01 21:01:24455#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
treib29e1b9b12015-11-11 08:50:56456 if (!has_all_permissions && (state == INSTALLED_NEWER) &&
457 extensions::util::IsExtensionSupervised(extension, profile_)) {
458 SupervisedUserServiceFactory::GetForProfile(profile_)
459 ->AddExtensionUpdateRequest(id, *extension->version());
treib8668a302015-10-05 16:21:53460 }
treib29e1b9b12015-11-11 08:50:56461#endif
treib8668a302015-10-05 16:21:53462 } else {
463 // The extension is not installed yet. Set it to enabled; we'll check for
treib29e1b9b12015-11-11 08:50:56464 // permission increase (more accurately, for a version change) when it's
465 // actually installed.
treibecc63c8d2015-09-07 16:34:47466 extension_service()->EnableExtension(id);
treib8668a302015-10-05 16:21:53467 }
treib29e1b9b12015-11-11 08:50:56468 } else if (!should_be_enabled) {
469 // Note that |disable_reasons| includes any pre-existing reasons that
470 // weren't explicitly removed above.
471 if (extension_service()->IsExtensionEnabled(id))
472 extension_service()->DisableExtension(id, disable_reasons);
473 else // Already disabled, just replace the disable reasons.
474 extension_prefs->ReplaceDisableReasons(id, disable_reasons);
treibecc63c8d2015-09-07 16:34:47475 }
476
treibecc63c8d2015-09-07 16:34:47477 // Update the incognito flag.
478 extensions::util::SetIsIncognitoEnabled(
479 id, profile_, extension_sync_data.incognito_enabled());
480 extension = nullptr; // No longer safe to use.
481
482 // Update the all urls flag.
483 if (extension_sync_data.all_urls_enabled() !=
484 ExtensionSyncData::BOOLEAN_UNSET) {
485 bool allowed = extension_sync_data.all_urls_enabled() ==
rdevlin.cronind01837b2016-08-17 01:37:18486 ExtensionSyncData::BOOLEAN_TRUE;
487 extensions::ScriptingPermissionsModifier::SetAllowedOnAllUrlsForSync(
488 allowed, profile_, id);
treibecc63c8d2015-09-07 16:34:47489 }
490
491 // Set app-specific data.
treib0c714f7c2015-07-08 10:04:58492 if (extension_sync_data.is_app()) {
493 if (extension_sync_data.app_launch_ordinal().IsValid() &&
494 extension_sync_data.page_ordinal().IsValid()) {
treib926ee2d2015-08-06 10:55:42495 AppSorting* app_sorting = ExtensionSystem::Get(profile_)->app_sorting();
496 app_sorting->SetAppLaunchOrdinal(
treib0c714f7c2015-07-08 10:04:58497 id,
498 extension_sync_data.app_launch_ordinal());
treib926ee2d2015-08-06 10:55:42499 app_sorting->SetPageOrdinal(id, extension_sync_data.page_ordinal());
treib0c714f7c2015-07-08 10:04:58500 }
501
treibc3494532015-07-21 14:51:45502 // The corresponding validation of this value during ExtensionSyncData
503 // population is in ExtensionSyncData::ToAppSpecifics.
treib0c714f7c2015-07-08 10:04:58504 if (extension_sync_data.launch_type() >= extensions::LAUNCH_TYPE_FIRST &&
505 extension_sync_data.launch_type() < extensions::NUM_LAUNCH_TYPES) {
506 extensions::SetLaunchType(
507 profile_, id, extension_sync_data.launch_type());
508 }
509
Giovanni Ortuño Urquidib7d775d2017-07-18 03:20:16510 if (!extension_sync_data.bookmark_app_url().empty()) {
511 // Handles creating and updating the bookmark app.
treib0c714f7c2015-07-08 10:04:58512 ApplyBookmarkAppSyncData(extension_sync_data);
Giovanni Ortuño Urquidib7d775d2017-07-18 03:20:16513 return;
514 }
treib0c714f7c2015-07-08 10:04:58515 }
516
treibecc63c8d2015-09-07 16:34:47517 // Finally, trigger installation/update as required.
518 bool check_for_updates = false;
treib29e1b9b12015-11-11 08:50:56519 if (state == INSTALLED_OUTDATED) {
treibecc63c8d2015-09-07 16:34:47520 // If the extension is installed but outdated, store the new version.
treib29e1b9b12015-11-11 08:50:56521 pending_updates_[id] =
522 PendingUpdate(extension_sync_data.version(), reenable_after_update);
523 check_for_updates = true;
524 } else if (state == NOT_INSTALLED) {
treibecc63c8d2015-09-07 16:34:47525 if (!extension_service()->pending_extension_manager()->AddFromSync(
526 id,
527 extension_sync_data.update_url(),
treibe960e282015-09-11 10:38:08528 extension_sync_data.version(),
treib227b2582015-12-09 09:28:26529 ShouldAllowInstall,
mamir192d7882016-06-22 17:10:16530 extension_sync_data.remote_install())) {
treibecc63c8d2015-09-07 16:34:47531 LOG(WARNING) << "Could not add pending extension for " << id;
532 // This means that the extension is already pending installation, with a
533 // non-INTERNAL location. Add to pending_sync_data, even though it will
534 // never be removed (we'll never install a syncable version of the
535 // extension), so that GetAllSyncData() continues to send it.
536 }
537 // Track pending extensions so that we can return them in GetAllSyncData().
treib227b2582015-12-09 09:28:26538 bundle->AddPendingExtensionData(extension_sync_data);
treibecc63c8d2015-09-07 16:34:47539 check_for_updates = true;
[email protected]f8aefb132013-10-30 09:29:52540 }
541
treibecc63c8d2015-09-07 16:34:47542 if (check_for_updates)
543 extension_service()->CheckForUpdatesSoon();
[email protected]f8aefb132013-10-30 09:29:52544}
545
treib0c714f7c2015-07-08 10:04:58546void ExtensionSyncService::ApplyBookmarkAppSyncData(
treib926ee2d2015-08-06 10:55:42547 const ExtensionSyncData& extension_sync_data) {
treib0c714f7c2015-07-08 10:04:58548 DCHECK(extension_sync_data.is_app());
[email protected]f8aefb132013-10-30 09:29:52549
[email protected]d93fcd5132014-04-24 08:42:45550 // Process bookmark app sync if necessary.
treib0c714f7c2015-07-08 10:04:58551 GURL bookmark_app_url(extension_sync_data.bookmark_app_url());
[email protected]d93fcd5132014-04-24 08:42:45552 if (!bookmark_app_url.is_valid() ||
treib0c714f7c2015-07-08 10:04:58553 extension_sync_data.uninstalled()) {
[email protected]d93fcd5132014-04-24 08:42:45554 return;
555 }
556
treib0c714f7c2015-07-08 10:04:58557 const Extension* extension =
treib35f9ed02015-08-10 10:38:44558 extension_service()->GetInstalledExtension(extension_sync_data.id());
[email protected]d93fcd5132014-04-24 08:42:45559
560 // Return if there are no bookmark app details that need updating.
treib0c714f7c2015-07-08 10:04:58561 if (extension &&
562 extension->non_localized_name() == extension_sync_data.name() &&
563 extension->description() ==
564 extension_sync_data.bookmark_app_description()) {
[email protected]d93fcd5132014-04-24 08:42:45565 return;
566 }
567
568 WebApplicationInfo web_app_info;
569 web_app_info.app_url = bookmark_app_url;
treib0c714f7c2015-07-08 10:04:58570 web_app_info.title = base::UTF8ToUTF16(extension_sync_data.name());
[email protected]d93fcd5132014-04-24 08:42:45571 web_app_info.description =
treib0c714f7c2015-07-08 10:04:58572 base::UTF8ToUTF16(extension_sync_data.bookmark_app_description());
573 if (!extension_sync_data.bookmark_app_icon_color().empty()) {
limasdf3aa01fa2015-11-01 19:39:14574 extensions::image_util::ParseHexColorString(
treib0c714f7c2015-07-08 10:04:58575 extension_sync_data.bookmark_app_icon_color(),
benwellsd4b64a72014-12-11 12:38:27576 &web_app_info.generated_icon_color);
577 }
treib0c714f7c2015-07-08 10:04:58578 for (const auto& icon : extension_sync_data.linked_icons()) {
benwellsed75d1b52015-04-29 02:39:19579 WebApplicationInfo::IconInfo icon_info;
580 icon_info.url = icon.url;
581 icon_info.width = icon.size;
582 icon_info.height = icon.size;
583 web_app_info.icons.push_back(icon_info);
584 }
[email protected]d93fcd5132014-04-24 08:42:45585
586 // If the bookmark app already exists, keep the old icons.
587 if (!extension) {
treib35f9ed02015-08-10 10:38:44588 CreateOrUpdateBookmarkApp(extension_service(), &web_app_info);
[email protected]d93fcd5132014-04-24 08:42:45589 } else {
[email protected]d93fcd5132014-04-24 08:42:45590 GetWebApplicationInfoFromApp(profile_,
591 extension,
592 base::Bind(&OnWebApplicationInfoLoaded,
593 web_app_info,
treib35f9ed02015-08-10 10:38:44594 extension_service()->AsWeakPtr()));
[email protected]d93fcd5132014-04-24 08:42:45595 }
596}
597
treib35f9ed02015-08-10 10:38:44598void ExtensionSyncService::SetSyncStartFlareForTesting(
[email protected]f8aefb132013-10-30 09:29:52599 const syncer::SyncableService::StartSyncFlare& flare) {
600 flare_ = flare;
601}
602
treib227b2582015-12-09 09:28:26603void ExtensionSyncService::DeleteThemeDoNotUse(const Extension& theme) {
604 DCHECK(theme.is_theme());
605 GetSyncBundle(syncer::EXTENSIONS)->PushSyncDeletion(
606 theme.id(), CreateSyncData(theme).GetSyncData());
607}
608
treib35f9ed02015-08-10 10:38:44609ExtensionService* ExtensionSyncService::extension_service() const {
610 return ExtensionSystem::Get(profile_)->extension_service();
611}
612
treib8a6d9892015-08-26 10:23:19613void ExtensionSyncService::OnExtensionInstalled(
614 content::BrowserContext* browser_context,
treibecc63c8d2015-09-07 16:34:47615 const Extension* extension,
treib8a6d9892015-08-26 10:23:19616 bool is_update) {
617 DCHECK_EQ(profile_, browser_context);
treibecc63c8d2015-09-07 16:34:47618 // Clear pending version if the installed one has caught up.
treib95e800c2015-10-13 15:48:31619 auto it = pending_updates_.find(extension->id());
620 if (it != pending_updates_.end()) {
621 int compare_result = extension->version()->CompareTo(it->second.version);
asargente48ab752016-03-12 00:59:20622 if (compare_result == 0 && it->second.grant_permissions_and_reenable) {
623 // The call to SyncExtensionChangeIfNeeded below will take care of syncing
624 // changes to this extension, so we don't want to trigger sync activity
625 // from the call to GrantPermissionsAndEnableExtension.
626 base::AutoReset<bool> ignore_updates(&ignore_updates_, true);
treib95e800c2015-10-13 15:48:31627 extension_service()->GrantPermissionsAndEnableExtension(extension);
asargente48ab752016-03-12 00:59:20628 }
treib95e800c2015-10-13 15:48:31629 if (compare_result >= 0)
630 pending_updates_.erase(it);
treibecc63c8d2015-09-07 16:34:47631 }
treib8a6d9892015-08-26 10:23:19632 SyncExtensionChangeIfNeeded(*extension);
633}
634
635void ExtensionSyncService::OnExtensionUninstalled(
636 content::BrowserContext* browser_context,
treibecc63c8d2015-09-07 16:34:47637 const Extension* extension,
treib8a6d9892015-08-26 10:23:19638 extensions::UninstallReason reason) {
639 DCHECK_EQ(profile_, browser_context);
640 // Don't bother syncing if the extension will be re-installed momentarily.
641 if (reason == extensions::UNINSTALL_REASON_REINSTALL ||
treib227b2582015-12-09 09:28:26642 !ShouldSync(*extension)) {
treib8a6d9892015-08-26 10:23:19643 return;
treib227b2582015-12-09 09:28:26644 }
treib8a6d9892015-08-26 10:23:19645
646 // TODO(tim): If we get here and IsSyncing is false, this will cause
647 // "back from the dead" style bugs, because sync will add-back the extension
648 // that was uninstalled here when MergeDataAndStartSyncing is called.
649 // See crbug.com/256795.
650 // Possible fix: Set NeedsSync here, then in MergeDataAndStartSyncing, if
651 // NeedsSync is set but the extension isn't installed, send a sync deletion.
treibb794dd52015-12-01 18:47:14652 if (!ignore_updates_) {
653 syncer::ModelType type =
654 extension->is_app() ? syncer::APPS : syncer::EXTENSIONS;
655 SyncBundle* bundle = GetSyncBundle(type);
656 if (bundle->IsSyncing()) {
657 bundle->PushSyncDeletion(extension->id(),
658 CreateSyncData(*extension).GetSyncData());
659 } else if (extension_service()->is_ready() && !flare_.is_null()) {
660 flare_.Run(type); // Tell sync to start ASAP.
661 }
treib8a6d9892015-08-26 10:23:19662 }
treibecc63c8d2015-09-07 16:34:47663
treib95e800c2015-10-13 15:48:31664 pending_updates_.erase(extension->id());
treib8a6d9892015-08-26 10:23:19665}
666
667void ExtensionSyncService::OnExtensionStateChanged(
668 const std::string& extension_id,
669 bool state) {
670 ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
671 const Extension* extension = registry->GetInstalledExtension(extension_id);
672 // We can get pref change notifications for extensions that aren't installed
673 // (yet). In that case, we'll pick up the change later via ExtensionRegistry
674 // observation (in OnExtensionInstalled).
675 if (extension)
676 SyncExtensionChangeIfNeeded(*extension);
677}
678
679void ExtensionSyncService::OnExtensionDisableReasonsChanged(
680 const std::string& extension_id,
681 int disabled_reasons) {
682 ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
683 const Extension* extension = registry->GetInstalledExtension(extension_id);
684 // We can get pref change notifications for extensions that aren't installed
685 // (yet). In that case, we'll pick up the change later via ExtensionRegistry
686 // observation (in OnExtensionInstalled).
687 if (extension)
688 SyncExtensionChangeIfNeeded(*extension);
689}
690
treib0c714f7c2015-07-08 10:04:58691SyncBundle* ExtensionSyncService::GetSyncBundle(syncer::ModelType type) {
692 return const_cast<SyncBundle*>(
693 const_cast<const ExtensionSyncService&>(*this).GetSyncBundle(type));
694}
695
696const SyncBundle* ExtensionSyncService::GetSyncBundle(
697 syncer::ModelType type) const {
treibc644a1c2015-07-13 08:37:04698 return (type == syncer::APPS) ? &app_sync_bundle_ : &extension_sync_bundle_;
699}
700
treibc3494532015-07-21 14:51:45701std::vector<ExtensionSyncData> ExtensionSyncService::GetLocalSyncDataList(
treib0feb8282016-01-18 10:18:15702 syncer::ModelType type) const {
treibecc63c8d2015-09-07 16:34:47703 // Collect the local state.
treib0c714f7c2015-07-08 10:04:58704 ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
treibc3494532015-07-21 14:51:45705 std::vector<ExtensionSyncData> data;
706 // TODO(treib, kalman): Should we be including blacklisted/blocked extensions
707 // here? I.e. just calling registry->GeneratedInstalledExtensionsSet()?
708 // It would be more consistent, but the danger is that the black/blocklist
709 // hasn't been updated on all clients by the time sync has kicked in -
710 // so it's safest not to. Take care to add any other extension lists here
711 // in the future if they are added.
treib0feb8282016-01-18 10:18:15712 FillSyncDataList(registry->enabled_extensions(), type, &data);
713 FillSyncDataList(registry->disabled_extensions(), type, &data);
714 FillSyncDataList(registry->terminated_extensions(), type, &data);
treibc3494532015-07-21 14:51:45715 return data;
treib0c714f7c2015-07-08 10:04:58716}
717
718void ExtensionSyncService::FillSyncDataList(
719 const ExtensionSet& extensions,
720 syncer::ModelType type,
treib0c714f7c2015-07-08 10:04:58721 std::vector<ExtensionSyncData>* sync_data_list) const {
treib0c714f7c2015-07-08 10:04:58722 for (const scoped_refptr<const Extension>& extension : extensions) {
treib0feb8282016-01-18 10:18:15723 if (IsCorrectSyncType(*extension, type) && ShouldSync(*extension)) {
treibecc63c8d2015-09-07 16:34:47724 // We should never have pending data for an installed extension.
725 DCHECK(!GetSyncBundle(type)->HasPendingExtensionData(extension->id()));
treib0c714f7c2015-07-08 10:04:58726 sync_data_list->push_back(CreateSyncData(*extension));
727 }
728 }
729}
treib227b2582015-12-09 09:28:26730
731bool ExtensionSyncService::ShouldSync(const Extension& extension) const {
732 // Themes are handled by the ThemeSyncableService.
733 return extensions::util::ShouldSync(&extension, profile_) &&
734 !extension.is_theme();
735}