blob: 494ffdca3ead1311b5fbee68eeeb591cfa07ee56 [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
[email protected]f8aefb132013-10-30 09:29:527#include "base/basictypes.h"
[email protected]d93fcd5132014-04-24 08:42:458#include "base/strings/utf_string_conversions.h"
[email protected]d93fcd5132014-04-24 08:42:459#include "chrome/browser/extensions/bookmark_app_helper.h"
[email protected]f8aefb132013-10-30 09:29:5210#include "chrome/browser/extensions/extension_service.h"
[email protected]f8aefb132013-10-30 09:29:5211#include "chrome/browser/extensions/extension_sync_data.h"
12#include "chrome/browser/extensions/extension_sync_service_factory.h"
13#include "chrome/browser/extensions/extension_util.h"
[email protected]a9f74a62014-01-10 06:48:3614#include "chrome/browser/extensions/launch_util.h"
[email protected]f8aefb132013-10-30 09:29:5215#include "chrome/browser/profiles/profile.h"
16#include "chrome/browser/sync/glue/sync_start_util.h"
[email protected]d93fcd5132014-04-24 08:42:4517#include "chrome/common/extensions/extension_constants.h"
[email protected]f8aefb132013-10-30 09:29:5218#include "chrome/common/extensions/sync_helper.h"
[email protected]d93fcd5132014-04-24 08:42:4519#include "chrome/common/web_application_info.h"
[email protected]74474042013-11-21 12:03:5420#include "extensions/browser/app_sorting.h"
[email protected]489db0842014-01-22 18:20:0321#include "extensions/browser/extension_prefs.h"
[email protected]ca975942014-01-07 12:06:4722#include "extensions/browser/extension_registry.h"
treib926ee2d2015-08-06 10:55:4223#include "extensions/browser/extension_system.h"
[email protected]411f8ae2014-05-22 11:12:2324#include "extensions/browser/extension_util.h"
[email protected]e43c61f2014-07-20 21:46:3425#include "extensions/browser/uninstall_reason.h"
[email protected]e4452d32013-11-15 23:07:4126#include "extensions/common/extension.h"
treib0c714f7c2015-07-08 10:04:5827#include "extensions/common/extension_set.h"
benwellsd4b64a72014-12-11 12:38:2728#include "extensions/common/image_util.h"
treib8668a302015-10-05 16:21:5329#include "extensions/common/permissions/permission_message_provider.h"
30#include "extensions/common/permissions/permissions_data.h"
[email protected]f8aefb132013-10-30 09:29:5231#include "sync/api/sync_change.h"
32#include "sync/api/sync_error_factory.h"
33
treib40d3ad92015-10-20 18:15:4234#if defined(ENABLE_SUPERVISED_USERS)
35#include "chrome/browser/supervised_user/supervised_user_service.h"
36#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
37#endif
38
treib926ee2d2015-08-06 10:55:4239using extensions::AppSorting;
[email protected]f8aefb132013-10-30 09:29:5240using extensions::Extension;
41using extensions::ExtensionPrefs;
[email protected]ca975942014-01-07 12:06:4742using extensions::ExtensionRegistry;
treib0c714f7c2015-07-08 10:04:5843using extensions::ExtensionSet;
rdevlin.cronind1aa8522015-02-13 00:25:5744using extensions::ExtensionSyncData;
treib926ee2d2015-08-06 10:55:4245using extensions::ExtensionSystem;
treib0c714f7c2015-07-08 10:04:5846using extensions::SyncBundle;
[email protected]f8aefb132013-10-30 09:29:5247
[email protected]d93fcd5132014-04-24 08:42:4548namespace {
49
50void OnWebApplicationInfoLoaded(
51 WebApplicationInfo synced_info,
52 base::WeakPtr<ExtensionService> extension_service,
53 const WebApplicationInfo& loaded_info) {
54 DCHECK_EQ(synced_info.app_url, loaded_info.app_url);
55
56 if (!extension_service)
57 return;
58
59 // Use the old icons if they exist.
60 synced_info.icons = loaded_info.icons;
benwellsd4b64a72014-12-11 12:38:2761 CreateOrUpdateBookmarkApp(extension_service.get(), &synced_info);
[email protected]d93fcd5132014-04-24 08:42:4562}
63
rdevlin.cronind1aa8522015-02-13 00:25:5764// Returns the pref value for "all urls enabled" for the given extension id.
65ExtensionSyncData::OptionalBoolean GetAllowedOnAllUrlsOptionalBoolean(
66 const std::string& extension_id,
67 content::BrowserContext* context) {
68 bool allowed_on_all_urls =
69 extensions::util::AllowedScriptingOnAllUrls(extension_id, context);
70 // If the extension is not allowed on all urls (which is not the default),
71 // then we have to sync the preference.
72 if (!allowed_on_all_urls)
73 return ExtensionSyncData::BOOLEAN_FALSE;
74
75 // If the user has explicitly set a value, then we sync it.
76 if (extensions::util::HasSetAllowedScriptingOnAllUrls(extension_id, context))
77 return ExtensionSyncData::BOOLEAN_TRUE;
78
79 // Otherwise, unset.
80 return ExtensionSyncData::BOOLEAN_UNSET;
81}
82
treib0c714f7c2015-07-08 10:04:5883// Returns true if the sync type of |extension| matches |type|.
84bool IsCorrectSyncType(const Extension& extension, syncer::ModelType type) {
treibc644a1c2015-07-13 08:37:0485 return (type == syncer::EXTENSIONS && extension.is_extension()) ||
86 (type == syncer::APPS && extension.is_app());
treib0c714f7c2015-07-08 10:04:5887}
88
treibc3494532015-07-21 14:51:4589syncer::SyncDataList ToSyncerSyncDataList(
90 const std::vector<ExtensionSyncData>& data) {
91 syncer::SyncDataList result;
92 result.reserve(data.size());
93 for (const ExtensionSyncData& item : data)
94 result.push_back(item.GetSyncData());
95 return result;
96}
97
[email protected]d93fcd5132014-04-24 08:42:4598} // namespace
99
treib95e800c2015-10-13 15:48:31100struct ExtensionSyncService::PendingUpdate {
101 PendingUpdate() : grant_permissions_and_reenable(false) {}
102 PendingUpdate(const base::Version& version,
103 bool grant_permissions_and_reenable)
104 : version(version),
105 grant_permissions_and_reenable(grant_permissions_and_reenable) {}
106
107 base::Version version;
108 bool grant_permissions_and_reenable;
109};
110
treib35f9ed02015-08-10 10:38:44111ExtensionSyncService::ExtensionSyncService(Profile* profile)
[email protected]f8aefb132013-10-30 09:29:52112 : profile_(profile),
treib8a6d9892015-08-26 10:23:19113 registry_observer_(this),
114 prefs_observer_(this),
treib35f9ed02015-08-10 10:38:44115 flare_(sync_start_util::GetFlareForSyncableService(profile->GetPath())) {
treib8a6d9892015-08-26 10:23:19116 registry_observer_.Add(ExtensionRegistry::Get(profile_));
117 prefs_observer_.Add(ExtensionPrefs::Get(profile_));
[email protected]f8aefb132013-10-30 09:29:52118}
119
treib8a6d9892015-08-26 10:23:19120ExtensionSyncService::~ExtensionSyncService() {
121}
[email protected]f8aefb132013-10-30 09:29:52122
123// static
macourteau86b29d82015-01-23 13:24:45124ExtensionSyncService* ExtensionSyncService::Get(
125 content::BrowserContext* context) {
126 return ExtensionSyncServiceFactory::GetForBrowserContext(context);
[email protected]f8aefb132013-10-30 09:29:52127}
128
treib0c714f7c2015-07-08 10:04:58129void ExtensionSyncService::SyncExtensionChangeIfNeeded(
130 const Extension& extension) {
treibc644a1c2015-07-13 08:37:04131 if (!extensions::util::ShouldSync(&extension, profile_))
treib0c714f7c2015-07-08 10:04:58132 return;
133
134 syncer::ModelType type =
135 extension.is_app() ? syncer::APPS : syncer::EXTENSIONS;
136 SyncBundle* bundle = GetSyncBundle(type);
treibc3494532015-07-21 14:51:45137 if (bundle->IsSyncing()) {
138 bundle->PushSyncAddOrUpdate(extension.id(),
139 CreateSyncData(extension).GetSyncData());
140 DCHECK(!ExtensionPrefs::Get(profile_)->NeedsSync(extension.id()));
141 } else {
142 ExtensionPrefs::Get(profile_)->SetNeedsSync(extension.id(), true);
treib35f9ed02015-08-10 10:38:44143 if (extension_service()->is_ready() && !flare_.is_null())
treibc3494532015-07-21 14:51:45144 flare_.Run(type); // Tell sync to start ASAP.
145 }
treib0c714f7c2015-07-08 10:04:58146}
147
treib40d3ad92015-10-20 18:15:42148bool ExtensionSyncService::HasPendingReenable(
149 const std::string& id,
150 const base::Version& version) const {
151 auto it = pending_updates_.find(id);
152 if (it == pending_updates_.end())
153 return false;
154 const PendingUpdate& pending = it->second;
155 return pending.version.Equals(version) &&
156 pending.grant_permissions_and_reenable;
157}
158
[email protected]f8aefb132013-10-30 09:29:52159syncer::SyncMergeResult ExtensionSyncService::MergeDataAndStartSyncing(
160 syncer::ModelType type,
161 const syncer::SyncDataList& initial_sync_data,
162 scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
163 scoped_ptr<syncer::SyncErrorFactory> sync_error_factory) {
164 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);
treibc3494532015-07-21 14:51:45169 bundle->StartSyncing(sync_processor.Pass());
[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) {
174 scoped_ptr<ExtensionSyncData> extension_sync_data(
175 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
treibc3494532015-07-21 14:51:45184 // Now push those local changes to sync.
185 // TODO(treib,kalman): We should only have to send out changes for extensions
186 // which have NeedsSync set (i.e. |GetLocalSyncDataList(type, false)|). That
187 // makes some sync_integration_tests fail though - figure out why and fix it!
188 std::vector<ExtensionSyncData> data_list = GetLocalSyncDataList(type, true);
189 bundle->PushSyncDataList(ToSyncerSyncDataList(data_list));
190 for (const ExtensionSyncData& data : data_list)
191 ExtensionPrefs::Get(profile_)->SetNeedsSync(data.id(), false);
[email protected]f8aefb132013-10-30 09:29:52192
treibc3494532015-07-21 14:51:45193 if (type == syncer::APPS)
treib926ee2d2015-08-06 10:55:42194 ExtensionSystem::Get(profile_)->app_sorting()->FixNTPOrdinalCollisions();
[email protected]f8aefb132013-10-30 09:29:52195
196 return syncer::SyncMergeResult(type);
197}
198
199void ExtensionSyncService::StopSyncing(syncer::ModelType type) {
treib0c714f7c2015-07-08 10:04:58200 GetSyncBundle(type)->Reset();
[email protected]f8aefb132013-10-30 09:29:52201}
202
203syncer::SyncDataList ExtensionSyncService::GetAllSyncData(
204 syncer::ModelType type) const {
treibc3494532015-07-21 14:51:45205 const SyncBundle* bundle = GetSyncBundle(type);
206 if (!bundle->IsSyncing())
207 return syncer::SyncDataList();
208
treib926ee2d2015-08-06 10:55:42209 std::vector<ExtensionSyncData> sync_data_list =
treibc3494532015-07-21 14:51:45210 GetLocalSyncDataList(type, true);
211
treibecc63c8d2015-09-07 16:34:47212 // Add pending data (where the local extension is not installed yet).
213 std::vector<ExtensionSyncData> pending_extensions =
214 bundle->GetPendingExtensionData();
treibc3494532015-07-21 14:51:45215 sync_data_list.insert(sync_data_list.begin(),
216 pending_extensions.begin(),
217 pending_extensions.end());
218
219 return ToSyncerSyncDataList(sync_data_list);
[email protected]f8aefb132013-10-30 09:29:52220}
221
222syncer::SyncError ExtensionSyncService::ProcessSyncChanges(
223 const tracked_objects::Location& from_here,
224 const syncer::SyncChangeList& change_list) {
treib0c714f7c2015-07-08 10:04:58225 for (const syncer::SyncChange& sync_change : change_list) {
treibc3494532015-07-21 14:51:45226 scoped_ptr<ExtensionSyncData> extension_sync_data(
227 ExtensionSyncData::CreateFromSyncChange(sync_change));
228 if (extension_sync_data)
229 ApplySyncData(*extension_sync_data);
[email protected]f8aefb132013-10-30 09:29:52230 }
231
treib926ee2d2015-08-06 10:55:42232 ExtensionSystem::Get(profile_)->app_sorting()->FixNTPOrdinalCollisions();
[email protected]f8aefb132013-10-30 09:29:52233
234 return syncer::SyncError();
235}
236
treib0c714f7c2015-07-08 10:04:58237ExtensionSyncData ExtensionSyncService::CreateSyncData(
treib926ee2d2015-08-06 10:55:42238 const Extension& extension) const {
treib35f9ed02015-08-10 10:38:44239 const ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile_);
treib8a6d9892015-08-26 10:23:19240 // Query the enabled state from ExtensionPrefs rather than
241 // ExtensionService::IsExtensionEnabled - the latter uses ExtensionRegistry
242 // which might not have been updated yet (ExtensionPrefs are updated first,
243 // and we're listening for changes to these).
244 bool enabled = !extension_prefs->IsExtensionDisabled(extension.id());
245 enabled = enabled &&
246 !extension_prefs->IsExternalExtensionUninstalled(extension.id());
247 // Blacklisted extensions are not marked as disabled in ExtensionPrefs.
248 enabled = enabled &&
249 extension_prefs->GetExtensionBlacklistState(extension.id()) ==
250 extensions::NOT_BLACKLISTED;
treib35f9ed02015-08-10 10:38:44251 int disable_reasons = extension_prefs->GetDisableReasons(extension.id());
treib0c714f7c2015-07-08 10:04:58252 bool incognito_enabled = extensions::util::IsIncognitoEnabled(extension.id(),
253 profile_);
254 bool remote_install =
treib35f9ed02015-08-10 10:38:44255 extension_prefs->HasDisableReason(extension.id(),
256 Extension::DISABLE_REMOTE_INSTALL);
treib0c714f7c2015-07-08 10:04:58257 ExtensionSyncData::OptionalBoolean allowed_on_all_url =
258 GetAllowedOnAllUrlsOptionalBoolean(extension.id(), profile_);
treibecc63c8d2015-09-07 16:34:47259 AppSorting* app_sorting = ExtensionSystem::Get(profile_)->app_sorting();
260
261 ExtensionSyncData result = extension.is_app()
262 ? ExtensionSyncData(
263 extension, enabled, disable_reasons, incognito_enabled,
264 remote_install, allowed_on_all_url,
265 app_sorting->GetAppLaunchOrdinal(extension.id()),
266 app_sorting->GetPageOrdinal(extension.id()),
267 extensions::GetLaunchTypePrefValue(extension_prefs,
268 extension.id()))
269 : ExtensionSyncData(
270 extension, enabled, disable_reasons, incognito_enabled,
271 remote_install, allowed_on_all_url);
272
273 // If there's a pending update, send the new version to sync instead of the
274 // installed one.
treib95e800c2015-10-13 15:48:31275 auto it = pending_updates_.find(extension.id());
276 if (it != pending_updates_.end()) {
277 const base::Version& version = it->second.version;
treibecc63c8d2015-09-07 16:34:47278 // If we have a pending version, it should be newer than the installed one.
279 DCHECK_EQ(-1, extension.version()->CompareTo(version));
280 result.set_version(version);
treib95e800c2015-10-13 15:48:31281 // If we'll re-enable the extension once it's updated, also send that back
282 // to sync.
283 if (it->second.grant_permissions_and_reenable)
284 result.set_enabled(true);
treib0c714f7c2015-07-08 10:04:58285 }
treibecc63c8d2015-09-07 16:34:47286 return result;
[email protected]f8aefb132013-10-30 09:29:52287}
288
treibecc63c8d2015-09-07 16:34:47289void ExtensionSyncService::ApplySyncData(
rdevlin.cronind1aa8522015-02-13 00:25:57290 const ExtensionSyncData& extension_sync_data) {
treibc3494532015-07-21 14:51:45291 syncer::ModelType type = extension_sync_data.is_app() ? syncer::APPS
292 : syncer::EXTENSIONS;
treibecc63c8d2015-09-07 16:34:47293 const std::string& id = extension_sync_data.id();
294 // Note: |extension| may be null if it hasn't been installed yet.
295 const Extension* extension =
296 ExtensionRegistry::Get(profile_)->GetInstalledExtension(id);
297 // TODO(bolms): we should really handle this better. The particularly bad
298 // case is where an app becomes an extension or vice versa, and we end up with
299 // a zombie extension that won't go away.
300 // TODO(treib): Is this still true?
301 if (extension && !IsCorrectSyncType(*extension, type))
302 return;
303
treibc3494532015-07-21 14:51:45304 SyncBundle* bundle = GetSyncBundle(type);
treibecc63c8d2015-09-07 16:34:47305 // Forward to the bundle. This will just update the list of synced extensions.
treibc3494532015-07-21 14:51:45306 bundle->ApplySyncData(extension_sync_data);
307
treibecc63c8d2015-09-07 16:34:47308 // Handle uninstalls first.
309 if (extension_sync_data.uninstalled()) {
310 if (!ExtensionService::UninstallExtensionHelper(
311 extension_service(), id, extensions::UNINSTALL_REASON_SYNC)) {
312 LOG(WARNING) << "Could not uninstall extension " << id << " for sync";
313 }
314 return;
315 }
treib0c714f7c2015-07-08 10:04:58316
treibecc63c8d2015-09-07 16:34:47317 // Extension from sync was uninstalled by the user as an external extension.
318 // Honor user choice and skip installation/enabling.
319 // TODO(treib): Should we still apply pref changes?
treib8668a302015-10-05 16:21:53320 ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile_);
321 if (extension_prefs->IsExternalExtensionUninstalled(id)) {
treibecc63c8d2015-09-07 16:34:47322 LOG(WARNING) << "Extension with id " << id
323 << " from sync was uninstalled as external extension";
324 return;
325 }
326
327 int version_compare_result = extension ?
328 extension->version()->CompareTo(extension_sync_data.version()) : 0;
329
330 // Enable/disable the extension.
treib95e800c2015-10-13 15:48:31331 bool reenable_after_update = false;
treibecc63c8d2015-09-07 16:34:47332 if (extension_sync_data.enabled()) {
333 DCHECK(!extension_sync_data.disable_reasons());
334
treib8668a302015-10-05 16:21:53335 if (extension) {
336 // Only grant permissions if the sync data explicitly sets the disable
337 // reasons to Extension::DISABLE_NONE (as opposed to the legacy (<M45)
338 // case where they're not set at all), and if the version from sync
339 // matches our local one.
340 bool grant_permissions = extension_sync_data.supports_disable_reasons() &&
341 (version_compare_result == 0);
342 if (grant_permissions) {
343 extension_service()->GrantPermissionsAndEnableExtension(extension);
treib95e800c2015-10-13 15:48:31344 } else if (!extension_service()->IsExtensionEnabled(extension->id())) {
treib8668a302015-10-05 16:21:53345 // Only enable if the extension already has all required permissions.
346 scoped_ptr<const extensions::PermissionSet> granted_permissions =
347 extension_prefs->GetGrantedPermissions(extension->id());
348 DCHECK(granted_permissions.get());
349 bool is_privilege_increase =
350 extensions::PermissionMessageProvider::Get()->IsPrivilegeIncrease(
351 *granted_permissions,
352 extension->permissions_data()->active_permissions(),
353 extension->GetType());
354 if (!is_privilege_increase)
355 extension_service()->EnableExtension(id);
treib95e800c2015-10-13 15:48:31356 else if (extension_sync_data.supports_disable_reasons())
357 reenable_after_update = true;
treib40d3ad92015-10-20 18:15:42358
359#if defined(ENABLE_SUPERVISED_USERS)
360 if (is_privilege_increase && version_compare_result > 0 &&
361 extensions::util::IsExtensionSupervised(extension, profile_)) {
362 SupervisedUserServiceFactory::GetForProfile(profile_)
363 ->AddExtensionUpdateRequest(id, *extension->version());
364 }
365#endif
treib8668a302015-10-05 16:21:53366 }
367 } else {
368 // The extension is not installed yet. Set it to enabled; we'll check for
369 // permission increase when it's actually installed.
treibecc63c8d2015-09-07 16:34:47370 extension_service()->EnableExtension(id);
treib8668a302015-10-05 16:21:53371 }
treibecc63c8d2015-09-07 16:34:47372 } else {
373 int disable_reasons = extension_sync_data.disable_reasons();
374 if (extension_sync_data.remote_install()) {
375 if (!(disable_reasons & Extension::DISABLE_REMOTE_INSTALL)) {
treib95e800c2015-10-13 15:48:31376 // Since disabled reasons are synced since M45, DISABLE_REMOTE_INSTALL
377 // should be among them already.
treibecc63c8d2015-09-07 16:34:47378 DCHECK(!extension_sync_data.supports_disable_reasons());
379 disable_reasons |= Extension::DISABLE_REMOTE_INSTALL;
380 }
381 } else if (!extension_sync_data.supports_disable_reasons()) {
382 // Legacy case (<M45), from before we synced disable reasons (see
383 // crbug.com/484214).
384 disable_reasons = Extension::DISABLE_UNKNOWN_FROM_SYNC;
385 }
386
387 // In the non-legacy case (>=M45), clear any existing disable reasons first.
388 // Otherwise sync can't remove just some of them.
389 if (extension_sync_data.supports_disable_reasons())
treib8668a302015-10-05 16:21:53390 extension_prefs->ClearDisableReasons(id);
treibecc63c8d2015-09-07 16:34:47391
392 extension_service()->DisableExtension(id, disable_reasons);
393 }
394
395 // If the target extension has already been installed ephemerally, it can
396 // be promoted to a regular installed extension and downloading from the Web
397 // Store is not necessary.
398 if (extension && extensions::util::IsEphemeralApp(id, profile_))
399 extension_service()->PromoteEphemeralApp(extension, true);
400
401 // Cache whether the extension was already installed because setting the
402 // incognito flag invalidates the |extension| pointer (it reloads the
403 // extension).
404 bool extension_installed = (extension != nullptr);
405
406 // Update the incognito flag.
407 extensions::util::SetIsIncognitoEnabled(
408 id, profile_, extension_sync_data.incognito_enabled());
409 extension = nullptr; // No longer safe to use.
410
411 // Update the all urls flag.
412 if (extension_sync_data.all_urls_enabled() !=
413 ExtensionSyncData::BOOLEAN_UNSET) {
414 bool allowed = extension_sync_data.all_urls_enabled() ==
415 ExtensionSyncData::BOOLEAN_TRUE;
416 extensions::util::SetAllowedScriptingOnAllUrls(id, profile_, allowed);
417 }
418
419 // Set app-specific data.
treib0c714f7c2015-07-08 10:04:58420 if (extension_sync_data.is_app()) {
421 if (extension_sync_data.app_launch_ordinal().IsValid() &&
422 extension_sync_data.page_ordinal().IsValid()) {
treib926ee2d2015-08-06 10:55:42423 AppSorting* app_sorting = ExtensionSystem::Get(profile_)->app_sorting();
424 app_sorting->SetAppLaunchOrdinal(
treib0c714f7c2015-07-08 10:04:58425 id,
426 extension_sync_data.app_launch_ordinal());
treib926ee2d2015-08-06 10:55:42427 app_sorting->SetPageOrdinal(id, extension_sync_data.page_ordinal());
treib0c714f7c2015-07-08 10:04:58428 }
429
treibc3494532015-07-21 14:51:45430 // The corresponding validation of this value during ExtensionSyncData
431 // population is in ExtensionSyncData::ToAppSpecifics.
treib0c714f7c2015-07-08 10:04:58432 if (extension_sync_data.launch_type() >= extensions::LAUNCH_TYPE_FIRST &&
433 extension_sync_data.launch_type() < extensions::NUM_LAUNCH_TYPES) {
434 extensions::SetLaunchType(
435 profile_, id, extension_sync_data.launch_type());
436 }
437
438 if (!extension_sync_data.bookmark_app_url().empty())
439 ApplyBookmarkAppSyncData(extension_sync_data);
440 }
441
treibecc63c8d2015-09-07 16:34:47442 // Finally, trigger installation/update as required.
443 bool check_for_updates = false;
444 if (extension_installed) {
445 // If the extension is installed but outdated, store the new version.
446 if (version_compare_result < 0) {
treib95e800c2015-10-13 15:48:31447 pending_updates_[id] = PendingUpdate(extension_sync_data.version(),
448 reenable_after_update);
treibecc63c8d2015-09-07 16:34:47449 check_for_updates = true;
450 }
451 } else {
452 if (!extension_service()->pending_extension_manager()->AddFromSync(
453 id,
454 extension_sync_data.update_url(),
treibe960e282015-09-11 10:38:08455 extension_sync_data.version(),
treibecc63c8d2015-09-07 16:34:47456 extensions::sync_helper::IsSyncable,
457 extension_sync_data.remote_install(),
458 extension_sync_data.installed_by_custodian())) {
459 LOG(WARNING) << "Could not add pending extension for " << id;
460 // This means that the extension is already pending installation, with a
461 // non-INTERNAL location. Add to pending_sync_data, even though it will
462 // never be removed (we'll never install a syncable version of the
463 // extension), so that GetAllSyncData() continues to send it.
464 }
465 // Track pending extensions so that we can return them in GetAllSyncData().
466 bundle->AddPendingExtensionData(id, extension_sync_data);
467 check_for_updates = true;
[email protected]f8aefb132013-10-30 09:29:52468 }
469
treibecc63c8d2015-09-07 16:34:47470 if (check_for_updates)
471 extension_service()->CheckForUpdatesSoon();
[email protected]f8aefb132013-10-30 09:29:52472}
473
treib0c714f7c2015-07-08 10:04:58474void ExtensionSyncService::ApplyBookmarkAppSyncData(
treib926ee2d2015-08-06 10:55:42475 const ExtensionSyncData& extension_sync_data) {
treib0c714f7c2015-07-08 10:04:58476 DCHECK(extension_sync_data.is_app());
[email protected]f8aefb132013-10-30 09:29:52477
[email protected]d93fcd5132014-04-24 08:42:45478 // Process bookmark app sync if necessary.
treib0c714f7c2015-07-08 10:04:58479 GURL bookmark_app_url(extension_sync_data.bookmark_app_url());
[email protected]d93fcd5132014-04-24 08:42:45480 if (!bookmark_app_url.is_valid() ||
treib0c714f7c2015-07-08 10:04:58481 extension_sync_data.uninstalled()) {
[email protected]d93fcd5132014-04-24 08:42:45482 return;
483 }
484
treib0c714f7c2015-07-08 10:04:58485 const Extension* extension =
treib35f9ed02015-08-10 10:38:44486 extension_service()->GetInstalledExtension(extension_sync_data.id());
[email protected]d93fcd5132014-04-24 08:42:45487
488 // Return if there are no bookmark app details that need updating.
treib0c714f7c2015-07-08 10:04:58489 if (extension &&
490 extension->non_localized_name() == extension_sync_data.name() &&
491 extension->description() ==
492 extension_sync_data.bookmark_app_description()) {
[email protected]d93fcd5132014-04-24 08:42:45493 return;
494 }
495
496 WebApplicationInfo web_app_info;
497 web_app_info.app_url = bookmark_app_url;
treib0c714f7c2015-07-08 10:04:58498 web_app_info.title = base::UTF8ToUTF16(extension_sync_data.name());
[email protected]d93fcd5132014-04-24 08:42:45499 web_app_info.description =
treib0c714f7c2015-07-08 10:04:58500 base::UTF8ToUTF16(extension_sync_data.bookmark_app_description());
501 if (!extension_sync_data.bookmark_app_icon_color().empty()) {
benwellsd4b64a72014-12-11 12:38:27502 extensions::image_util::ParseCSSColorString(
treib0c714f7c2015-07-08 10:04:58503 extension_sync_data.bookmark_app_icon_color(),
benwellsd4b64a72014-12-11 12:38:27504 &web_app_info.generated_icon_color);
505 }
treib0c714f7c2015-07-08 10:04:58506 for (const auto& icon : extension_sync_data.linked_icons()) {
benwellsed75d1b52015-04-29 02:39:19507 WebApplicationInfo::IconInfo icon_info;
508 icon_info.url = icon.url;
509 icon_info.width = icon.size;
510 icon_info.height = icon.size;
511 web_app_info.icons.push_back(icon_info);
512 }
[email protected]d93fcd5132014-04-24 08:42:45513
514 // If the bookmark app already exists, keep the old icons.
515 if (!extension) {
treib35f9ed02015-08-10 10:38:44516 CreateOrUpdateBookmarkApp(extension_service(), &web_app_info);
[email protected]d93fcd5132014-04-24 08:42:45517 } else {
[email protected]d93fcd5132014-04-24 08:42:45518 GetWebApplicationInfoFromApp(profile_,
519 extension,
520 base::Bind(&OnWebApplicationInfoLoaded,
521 web_app_info,
treib35f9ed02015-08-10 10:38:44522 extension_service()->AsWeakPtr()));
[email protected]d93fcd5132014-04-24 08:42:45523 }
524}
525
treib35f9ed02015-08-10 10:38:44526void ExtensionSyncService::SetSyncStartFlareForTesting(
[email protected]f8aefb132013-10-30 09:29:52527 const syncer::SyncableService::StartSyncFlare& flare) {
528 flare_ = flare;
529}
530
treib35f9ed02015-08-10 10:38:44531ExtensionService* ExtensionSyncService::extension_service() const {
532 return ExtensionSystem::Get(profile_)->extension_service();
533}
534
treib8a6d9892015-08-26 10:23:19535void ExtensionSyncService::OnExtensionInstalled(
536 content::BrowserContext* browser_context,
treibecc63c8d2015-09-07 16:34:47537 const Extension* extension,
treib8a6d9892015-08-26 10:23:19538 bool is_update) {
539 DCHECK_EQ(profile_, browser_context);
treibecc63c8d2015-09-07 16:34:47540 // Clear pending version if the installed one has caught up.
treib95e800c2015-10-13 15:48:31541 auto it = pending_updates_.find(extension->id());
542 if (it != pending_updates_.end()) {
543 int compare_result = extension->version()->CompareTo(it->second.version);
544 if (compare_result == 0 && it->second.grant_permissions_and_reenable)
545 extension_service()->GrantPermissionsAndEnableExtension(extension);
546 if (compare_result >= 0)
547 pending_updates_.erase(it);
treibecc63c8d2015-09-07 16:34:47548 }
treib8a6d9892015-08-26 10:23:19549 SyncExtensionChangeIfNeeded(*extension);
550}
551
552void ExtensionSyncService::OnExtensionUninstalled(
553 content::BrowserContext* browser_context,
treibecc63c8d2015-09-07 16:34:47554 const Extension* extension,
treib8a6d9892015-08-26 10:23:19555 extensions::UninstallReason reason) {
556 DCHECK_EQ(profile_, browser_context);
557 // Don't bother syncing if the extension will be re-installed momentarily.
558 if (reason == extensions::UNINSTALL_REASON_REINSTALL ||
559 !extensions::util::ShouldSync(extension, profile_))
560 return;
561
562 // TODO(tim): If we get here and IsSyncing is false, this will cause
563 // "back from the dead" style bugs, because sync will add-back the extension
564 // that was uninstalled here when MergeDataAndStartSyncing is called.
565 // See crbug.com/256795.
566 // Possible fix: Set NeedsSync here, then in MergeDataAndStartSyncing, if
567 // NeedsSync is set but the extension isn't installed, send a sync deletion.
568 syncer::ModelType type =
569 extension->is_app() ? syncer::APPS : syncer::EXTENSIONS;
570 SyncBundle* bundle = GetSyncBundle(type);
571 if (bundle->IsSyncing()) {
572 bundle->PushSyncDeletion(extension->id(),
573 CreateSyncData(*extension).GetSyncData());
574 } else if (extension_service()->is_ready() && !flare_.is_null()) {
575 flare_.Run(type); // Tell sync to start ASAP.
576 }
treibecc63c8d2015-09-07 16:34:47577
treib95e800c2015-10-13 15:48:31578 pending_updates_.erase(extension->id());
treib8a6d9892015-08-26 10:23:19579}
580
581void ExtensionSyncService::OnExtensionStateChanged(
582 const std::string& extension_id,
583 bool state) {
584 ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
585 const Extension* extension = registry->GetInstalledExtension(extension_id);
586 // We can get pref change notifications for extensions that aren't installed
587 // (yet). In that case, we'll pick up the change later via ExtensionRegistry
588 // observation (in OnExtensionInstalled).
589 if (extension)
590 SyncExtensionChangeIfNeeded(*extension);
591}
592
593void ExtensionSyncService::OnExtensionDisableReasonsChanged(
594 const std::string& extension_id,
595 int disabled_reasons) {
596 ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
597 const Extension* extension = registry->GetInstalledExtension(extension_id);
598 // We can get pref change notifications for extensions that aren't installed
599 // (yet). In that case, we'll pick up the change later via ExtensionRegistry
600 // observation (in OnExtensionInstalled).
601 if (extension)
602 SyncExtensionChangeIfNeeded(*extension);
603}
604
treib0c714f7c2015-07-08 10:04:58605SyncBundle* ExtensionSyncService::GetSyncBundle(syncer::ModelType type) {
606 return const_cast<SyncBundle*>(
607 const_cast<const ExtensionSyncService&>(*this).GetSyncBundle(type));
608}
609
610const SyncBundle* ExtensionSyncService::GetSyncBundle(
611 syncer::ModelType type) const {
treibc644a1c2015-07-13 08:37:04612 return (type == syncer::APPS) ? &app_sync_bundle_ : &extension_sync_bundle_;
613}
614
treibc3494532015-07-21 14:51:45615std::vector<ExtensionSyncData> ExtensionSyncService::GetLocalSyncDataList(
616 syncer::ModelType type,
617 bool include_everything) const {
treibecc63c8d2015-09-07 16:34:47618 // Collect the local state.
treib0c714f7c2015-07-08 10:04:58619 ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
treibc3494532015-07-21 14:51:45620 std::vector<ExtensionSyncData> data;
621 // TODO(treib, kalman): Should we be including blacklisted/blocked extensions
622 // here? I.e. just calling registry->GeneratedInstalledExtensionsSet()?
623 // It would be more consistent, but the danger is that the black/blocklist
624 // hasn't been updated on all clients by the time sync has kicked in -
625 // so it's safest not to. Take care to add any other extension lists here
626 // in the future if they are added.
treib0c714f7c2015-07-08 10:04:58627 FillSyncDataList(
treibc3494532015-07-21 14:51:45628 registry->enabled_extensions(), type, include_everything, &data);
629 FillSyncDataList(
630 registry->disabled_extensions(), type, include_everything, &data);
631 FillSyncDataList(
632 registry->terminated_extensions(), type, include_everything, &data);
633 return data;
treib0c714f7c2015-07-08 10:04:58634}
635
636void ExtensionSyncService::FillSyncDataList(
637 const ExtensionSet& extensions,
638 syncer::ModelType type,
treibc3494532015-07-21 14:51:45639 bool include_everything,
treib0c714f7c2015-07-08 10:04:58640 std::vector<ExtensionSyncData>* sync_data_list) const {
treib0c714f7c2015-07-08 10:04:58641 for (const scoped_refptr<const Extension>& extension : extensions) {
642 if (IsCorrectSyncType(*extension, type) &&
treibc644a1c2015-07-13 08:37:04643 extensions::util::ShouldSync(extension.get(), profile_) &&
treibc3494532015-07-21 14:51:45644 (include_everything ||
645 ExtensionPrefs::Get(profile_)->NeedsSync(extension->id()))) {
treibecc63c8d2015-09-07 16:34:47646 // We should never have pending data for an installed extension.
647 DCHECK(!GetSyncBundle(type)->HasPendingExtensionData(extension->id()));
treib0c714f7c2015-07-08 10:04:58648 sync_data_list->push_back(CreateSyncData(*extension));
649 }
650 }
651}